blob: dc55790ce3903b7cf74af8d55f7e15dc4abbb03a [file] [log] [blame]
Hal Canary41194432019-09-09 17:03:38 -04001// Copyright 2019 Google LLC.
Brian Osmanb47704b2019-09-16 16:17:21 -04002// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
Hal Canary41194432019-09-09 17:03:38 -04003
4#include "experimental/skottie_ios/SkMetalViewBridge.h"
5#include "experimental/skottie_ios/SkottieMtkView.h"
6
7#include "include/gpu/GrContext.h"
8#include "include/gpu/GrContextOptions.h"
9
10#import <Metal/Metal.h>
11#import <MetalKit/MetalKit.h>
12#import <UIKit/UIKit.h>
13
14static UIStackView* make_skottie_stack(CGFloat width,
15 id<MTLDevice> metalDevice,
16 GrContext* grContext) {
17 UIStackView* stack = [[UIStackView alloc] init];
18 [stack setAxis:UILayoutConstraintAxisVertical];
19 [stack setDistribution:UIStackViewDistributionEqualSpacing];
20
21 NSBundle* mainBundle = [NSBundle mainBundle];
22 NSArray<NSString*>* paths = [mainBundle pathsForResourcesOfType:@"json"
23 inDirectory:nil];
24 constexpr CGFloat kSpacing = 2;
25 CGFloat totalHeight = kSpacing;
26 for (NSUInteger i = 0; i < [paths count]; ++i) {
27 NSString* path = [paths objectAtIndex:i];
28 NSData* content = [NSData dataWithContentsOfFile:path];
29 if (!content) {
30 NSLog(@"'%@' not found", path);
31 continue;
32 }
33 SkottieMtkView* skottieView = [[SkottieMtkView alloc] init];
34 if (![skottieView loadAnimation:content]) {
35 continue;
36 }
37 [skottieView setDevice:metalDevice];
38 [skottieView setGrContext:grContext];
39 SkMtkViewConfigForSkia(skottieView);
40 CGSize animSize = [skottieView size];
41 CGFloat height = animSize.width ? (width * animSize.height / animSize.width) : 0;
42 [skottieView setFrame:{{0, 0}, {width, height}}];
43 [skottieView setPreferredFramesPerSecond:30];
44 [[[skottieView heightAnchor] constraintEqualToConstant:height] setActive:true];
45 [[[skottieView widthAnchor] constraintEqualToConstant:width] setActive:true];
46 [stack addArrangedSubview:skottieView];
47 totalHeight += height + kSpacing;
48 }
49 [stack setFrame:{{0, 0}, {width, totalHeight}}];
50 return stack;
51}
52
53@interface AppViewController : UIViewController
54 @property (strong) id<MTLDevice> metalDevice;
55 @property (strong) UIStackView* stackView;
56@end
57
58@implementation AppViewController {
59 sk_sp<GrContext> fGrContext;
60}
61
62- (void)dealloc {
63 fGrContext = nullptr;
64 [super dealloc];
65}
66
67- (void)loadView {
68 [self setView:[[UIView alloc] init]];
69}
70
71- (void)viewDidLoad {
72 [super viewDidLoad];
73 if (!fGrContext) {
74 [self setMetalDevice:MTLCreateSystemDefaultDevice()];
75 if(![self metalDevice]) {
76 NSLog(@"Metal is not supported on this device");
77 return;
78 }
79 GrContextOptions grContextOptions; // set different options here.
80 fGrContext = SkMetalDeviceToGrContext([self metalDevice], grContextOptions);
81 }
82
83 [self setStackView:make_skottie_stack([[UIScreen mainScreen] bounds].size.width,
84 [self metalDevice], fGrContext.get())];
85
86 CGFloat statusBarHeight = [[UIApplication sharedApplication] statusBarFrame].size.height;
87 CGSize mainScreenSize = [[UIScreen mainScreen] bounds].size;
88 CGRect scrollViewBounds = {{0, statusBarHeight},
89 {mainScreenSize.width, mainScreenSize.height - statusBarHeight}};
90 UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:scrollViewBounds];
91 [scrollView setContentSize:[[self stackView] frame].size];
92 [scrollView addSubview:[self stackView]];
93 [scrollView setBackgroundColor:[UIColor blackColor]];
94
95 UIView* mainView = [self view];
96 [mainView setBounds:{{0, 0}, mainScreenSize}];
97 [mainView setBackgroundColor:[UIColor whiteColor]];
98 [mainView addSubview:scrollView];
99
100 UITapGestureRecognizer* tapGestureRecognizer = [[UITapGestureRecognizer alloc] init];
101 [tapGestureRecognizer addTarget:self action:@selector(handleTap:)];
102 [mainView addGestureRecognizer:tapGestureRecognizer];
103}
104
105- (void)handleTap:(UIGestureRecognizer*)sender {
106 if (![sender state] == UIGestureRecognizerStateEnded) {
107 return;
108 }
109 NSArray<UIView*>* subviews = [[self stackView] subviews];
110 for (NSUInteger i = 0; i < [subviews count]; ++i) {
111 UIView* subview = [subviews objectAtIndex:i];
112 if (![subview isKindOfClass:[SkottieMtkView class]]) {
113 continue;
114 }
115 SkottieMtkView* skottieView = (SkottieMtkView*)subview;
116 BOOL paused = [skottieView togglePaused];
117 [skottieView setEnableSetNeedsDisplay:paused];
118 [skottieView setPaused:paused];
119 }
120}
121@end
122
123@interface AppDelegate : UIResponder <UIApplicationDelegate>
124@property (strong, nonatomic) UIWindow* window;
125@end
126
127@implementation AppDelegate
128
129- (BOOL)application:(UIApplication*)app didFinishLaunchingWithOptions:(NSDictionary*)ops {
130 [self setWindow:[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]];
131 [[self window] setRootViewController:[[AppViewController alloc] init]];
132 [[self window] makeKeyAndVisible];
133 return YES;
134}
135@end
136
137int main(int argc, char* argv[]) {
138 @autoreleasepool {
139 return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
140 }
141}