This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
9.4 KiB
247 lines
9.4 KiB
/** |
|
* Copyright (c) 2015-present, Facebook, Inc. |
|
* |
|
* This source code is licensed under the MIT license found in the |
|
* LICENSE file in the root directory of this source tree. |
|
*/ |
|
|
|
#import "RCTTVView.h" |
|
|
|
#import "RCTAutoInsetsProtocol.h" |
|
#import "RCTBorderDrawing.h" |
|
#import "RCTBridge.h" |
|
#import "RCTConvert.h" |
|
#import "RCTEventDispatcher.h" |
|
#import "RCTLog.h" |
|
#import "RCTRootViewInternal.h" |
|
#import "RCTTVNavigationEventEmitter.h" |
|
#import "RCTUtils.h" |
|
#import "RCTView.h" |
|
#import "UIView+React.h" |
|
|
|
@implementation RCTTVView |
|
{ |
|
UITapGestureRecognizer *_selectRecognizer; |
|
} |
|
|
|
- (instancetype)initWithFrame:(CGRect)frame |
|
{ |
|
if (self = [super initWithFrame:frame]) { |
|
dispatch_once(&onceToken, ^{ |
|
defaultTVParallaxProperties = @{ |
|
@"enabled": @YES, |
|
@"shiftDistanceX": @2.0f, |
|
@"shiftDistanceY": @2.0f, |
|
@"tiltAngle": @0.05f, |
|
@"magnification": @1.0f, |
|
@"pressMagnification": @1.0f, |
|
@"pressDuration": @0.3f, |
|
@"pressDelay": @0.0f |
|
}; |
|
}); |
|
self.tvParallaxProperties = defaultTVParallaxProperties; |
|
} |
|
|
|
return self; |
|
} |
|
|
|
static NSDictionary* defaultTVParallaxProperties = nil; |
|
static dispatch_once_t onceToken; |
|
|
|
- (void)setTvParallaxProperties:(NSDictionary *)tvParallaxProperties { |
|
if (_tvParallaxProperties == nil) { |
|
_tvParallaxProperties = [defaultTVParallaxProperties copy]; |
|
return; |
|
} |
|
|
|
NSMutableDictionary *newParallaxProperties = [NSMutableDictionary dictionaryWithDictionary:_tvParallaxProperties]; |
|
for (NSString *k in [defaultTVParallaxProperties allKeys]) { |
|
if (tvParallaxProperties[k]) { |
|
newParallaxProperties[k] = tvParallaxProperties[k]; |
|
} |
|
} |
|
_tvParallaxProperties = [newParallaxProperties copy]; |
|
} |
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused) |
|
|
|
- (void)setIsTVSelectable:(BOOL)isTVSelectable { |
|
self->_isTVSelectable = isTVSelectable; |
|
if (isTVSelectable) { |
|
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc] |
|
initWithTarget:self |
|
action:@selector(handleSelect:)]; |
|
recognizer.allowedPressTypes = @[@(UIPressTypeSelect)]; |
|
_selectRecognizer = recognizer; |
|
[self addGestureRecognizer:_selectRecognizer]; |
|
} else { |
|
if(_selectRecognizer) { |
|
[self removeGestureRecognizer:_selectRecognizer]; |
|
} |
|
} |
|
} |
|
|
|
- (void)handleSelect:(__unused UIGestureRecognizer *)r |
|
{ |
|
if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) { |
|
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; |
|
float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue]; |
|
|
|
// Duration of press animation |
|
float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue]; |
|
|
|
// Delay of press animation |
|
float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue]; |
|
|
|
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]]; |
|
|
|
[UIView animateWithDuration:(pressDuration/2) |
|
animations:^{ |
|
self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification); |
|
} |
|
completion:^(__unused BOOL finished1){ |
|
[UIView animateWithDuration:(pressDuration/2) |
|
animations:^{ |
|
self.transform = CGAffineTransformMakeScale(magnification, magnification); |
|
} |
|
completion:^(__unused BOOL finished2) { |
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification |
|
object:@{@"eventType":@"select",@"tag":self.reactTag}]; |
|
}]; |
|
}]; |
|
|
|
} else { |
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification |
|
object:@{@"eventType":@"select",@"tag":self.reactTag}]; |
|
} |
|
} |
|
|
|
- (BOOL)isUserInteractionEnabled |
|
{ |
|
return YES; |
|
} |
|
|
|
- (BOOL)canBecomeFocused |
|
{ |
|
return (self.isTVSelectable); |
|
} |
|
|
|
- (void)addParallaxMotionEffects |
|
{ |
|
// Size of shift movements |
|
CGFloat const shiftDistanceX = [self.tvParallaxProperties[@"shiftDistanceX"] floatValue]; |
|
CGFloat const shiftDistanceY = [self.tvParallaxProperties[@"shiftDistanceY"] floatValue]; |
|
|
|
// Make horizontal movements shift the centre left and right |
|
UIInterpolatingMotionEffect *xShift = [[UIInterpolatingMotionEffect alloc] |
|
initWithKeyPath:@"center.x" |
|
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; |
|
xShift.minimumRelativeValue = @( shiftDistanceX * -1.0f); |
|
xShift.maximumRelativeValue = @( shiftDistanceX); |
|
|
|
// Make vertical movements shift the centre up and down |
|
UIInterpolatingMotionEffect *yShift = [[UIInterpolatingMotionEffect alloc] |
|
initWithKeyPath:@"center.y" |
|
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; |
|
yShift.minimumRelativeValue = @( shiftDistanceY * -1.0f); |
|
yShift.maximumRelativeValue = @( shiftDistanceY); |
|
|
|
// Size of tilt movements |
|
CGFloat const tiltAngle = [self.tvParallaxProperties[@"tiltAngle"] floatValue]; |
|
|
|
// Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation. |
|
UIInterpolatingMotionEffect *xTilt = [[UIInterpolatingMotionEffect alloc] |
|
initWithKeyPath:@"layer.transform" |
|
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; |
|
|
|
// CATransform3D value for minimumRelativeValue |
|
CATransform3D transMinimumTiltAboutY = CATransform3DIdentity; |
|
transMinimumTiltAboutY.m34 = 1.0 / 500; |
|
transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0); |
|
|
|
// CATransform3D value for minimumRelativeValue |
|
CATransform3D transMaximumTiltAboutY = CATransform3DIdentity; |
|
transMaximumTiltAboutY.m34 = 1.0 / 500; |
|
transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0); |
|
|
|
// Set the transform property boundaries for the interpolation |
|
xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutY]; |
|
xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutY]; |
|
|
|
// Now make vertical movements effect a rotation about the X axis for up and down rotation. |
|
UIInterpolatingMotionEffect *yTilt = [[UIInterpolatingMotionEffect alloc] |
|
initWithKeyPath:@"layer.transform" |
|
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; |
|
|
|
// CATransform3D value for minimumRelativeValue |
|
CATransform3D transMinimumTiltAboutX = CATransform3DIdentity; |
|
transMinimumTiltAboutX.m34 = 1.0 / 500; |
|
transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0); |
|
|
|
// CATransform3D value for minimumRelativeValue |
|
CATransform3D transMaximumTiltAboutX = CATransform3DIdentity; |
|
transMaximumTiltAboutX.m34 = 1.0 / 500; |
|
transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0); |
|
|
|
// Set the transform property boundaries for the interpolation |
|
yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutX]; |
|
yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutX]; |
|
|
|
// Add all of the motion effects to this group |
|
self.motionEffects = @[xShift, yShift, xTilt, yTilt]; |
|
|
|
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue]; |
|
|
|
[UIView animateWithDuration:0.2 animations:^{ |
|
self.transform = CGAffineTransformMakeScale(magnification, magnification); |
|
}]; |
|
} |
|
|
|
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator |
|
{ |
|
if (context.nextFocusedView == self && self.isTVSelectable ) { |
|
[self becomeFirstResponder]; |
|
[coordinator addCoordinatedAnimations:^(void){ |
|
if([self.tvParallaxProperties[@"enabled"] boolValue]) { |
|
[self addParallaxMotionEffects]; |
|
} |
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification |
|
object:@{@"eventType":@"focus",@"tag":self.reactTag}]; |
|
} completion:^(void){}]; |
|
} else { |
|
[coordinator addCoordinatedAnimations:^(void){ |
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification |
|
object:@{@"eventType":@"blur",@"tag":self.reactTag}]; |
|
[UIView animateWithDuration:0.2 animations:^{ |
|
self.transform = CGAffineTransformMakeScale(1, 1); |
|
}]; |
|
|
|
for (UIMotionEffect *effect in [self.motionEffects copy]){ |
|
[self removeMotionEffect:effect]; |
|
} |
|
} completion:^(void){}]; |
|
[self resignFirstResponder]; |
|
} |
|
} |
|
|
|
- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus |
|
{ |
|
_hasTVPreferredFocus = hasTVPreferredFocus; |
|
if (hasTVPreferredFocus) { |
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ |
|
UIView *rootview = self; |
|
while (![rootview isReactRootView] && rootview != nil) { |
|
rootview = [rootview superview]; |
|
} |
|
if (rootview == nil) return; |
|
|
|
rootview = [rootview superview]; |
|
|
|
[(RCTRootView *)rootview setReactPreferredFocusedView:self]; |
|
[rootview setNeedsFocusUpdate]; |
|
[rootview updateFocusIfNeeded]; |
|
}); |
|
} |
|
} |
|
|
|
@end
|
|
|