// Copyright 2019 Google LLC. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #include "experimental/skottie_ios/SkMetalViewBridge.h" #include "experimental/skottie_ios/SkottieMtkView.h" #include "include/gpu/GrContext.h" #include "include/gpu/GrContextOptions.h" #import #import #import static UIStackView* make_skottie_stack(CGFloat width, id metalDevice, id metalQueue, GrContext* grContext) { UIStackView* stack = [[UIStackView alloc] init]; [stack setAxis:UILayoutConstraintAxisVertical]; [stack setDistribution:UIStackViewDistributionEqualSpacing]; NSBundle* mainBundle = [NSBundle mainBundle]; NSArray* paths = [mainBundle pathsForResourcesOfType:@"json" inDirectory:@"data"]; constexpr CGFloat kSpacing = 2; CGFloat totalHeight = kSpacing; for (NSUInteger i = 0; i < [paths count]; ++i) { NSString* path = [paths objectAtIndex:i]; NSData* content = [NSData dataWithContentsOfFile:path]; if (!content) { NSLog(@"'%@' not found", path); continue; } SkottieMtkView* skottieView = [[SkottieMtkView alloc] init]; if (![skottieView loadAnimation:content]) { continue; } [skottieView setDevice:metalDevice]; [skottieView setQueue:metalQueue]; [skottieView setGrContext:grContext]; SkMtkViewConfigForSkia(skottieView); CGSize animSize = [skottieView size]; CGFloat height = animSize.width ? (width * animSize.height / animSize.width) : 0; [skottieView setFrame:{{0, 0}, {width, height}}]; [skottieView setPreferredFramesPerSecond:30]; [[[skottieView heightAnchor] constraintEqualToConstant:height] setActive:true]; [[[skottieView widthAnchor] constraintEqualToConstant:width] setActive:true]; [stack addArrangedSubview:skottieView]; totalHeight += height + kSpacing; } [stack setFrame:{{0, 0}, {width, totalHeight}}]; return stack; } @interface AppViewController : UIViewController @property (strong) id metalDevice; @property (strong) id metalQueue; @property (strong) UIStackView* stackView; @end @implementation AppViewController { sk_sp fGrContext; } - (void)loadView { [self setView:[[UIView alloc] init]]; } - (void)viewDidLoad { [super viewDidLoad]; if (!fGrContext) { [self setMetalDevice:MTLCreateSystemDefaultDevice()]; if(![self metalDevice]) { NSLog(@"Metal is not supported on this device"); return; } [self setMetalQueue:[[self metalDevice] newCommandQueue]]; GrContextOptions grContextOptions; // set different options here. fGrContext = SkMetalDeviceToGrContext([self metalDevice], [self metalQueue], grContextOptions); } [self setStackView:make_skottie_stack([[UIScreen mainScreen] bounds].size.width, [self metalDevice], [self metalQueue], fGrContext.get())]; CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height; CGSize mainScreenSize = [[UIScreen mainScreen] bounds].size; CGRect scrollViewBounds = {{0, statusBarHeight}, {mainScreenSize.width, mainScreenSize.height - statusBarHeight}}; UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:scrollViewBounds]; [scrollView setContentSize:[[self stackView] frame].size]; [scrollView addSubview:[self stackView]]; [scrollView setBackgroundColor:[UIColor blackColor]]; UIView* mainView = [self view]; [mainView setBounds:{{0, 0}, mainScreenSize}]; [mainView setBackgroundColor:[UIColor whiteColor]]; [mainView addSubview:scrollView]; UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc] init]; [tapGestureRecognizer addTarget:self action:@selector(handleTap:)]; [mainView addGestureRecognizer:tapGestureRecognizer]; } - (void)handleTap:(UIGestureRecognizer*)sender { if (![sender state] == UIGestureRecognizerStateEnded) { return; } NSArray* subviews = [[self stackView] subviews]; for (NSUInteger i = 0; i < [subviews count]; ++i) { UIView* subview = [subviews objectAtIndex:i]; if (![subview isKindOfClass:[SkottieMtkView class]]) { continue; } SkottieMtkView* skottieView = (SkottieMtkView*)subview; BOOL paused = [skottieView togglePaused]; [skottieView setEnableSetNeedsDisplay:paused]; [skottieView setPaused:paused]; } } @end @interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow* window; @end @implementation AppDelegate - (BOOL)application:(UIApplication*)app didFinishLaunchingWithOptions:(NSDictionary*)ops { [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]]; [[self window] setRootViewController:[[AppViewController alloc] init]]; [[self window] makeKeyAndVisible]; return YES; } @end int main(int argc, char* argv[]) { @autoreleasepool { return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }