Skip to content

Commit 1a64d15

Browse files
committed
implement custom sign up view controller
1 parent 5b83f0e commit 1a64d15

12 files changed

+429
-59
lines changed

FirebaseAuthUI/FIRAuthUI.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
@class FIRUser;
2525
@class FIREmailEntryViewController;
2626
@class FIRPasswordSignInViewController;
27+
@class FIRPasswordSignUpViewController;
2728

2829
NS_ASSUME_NONNULL_BEGIN
2930

@@ -66,9 +67,26 @@ typedef void (^FIRAuthUIResultCallback)(FIRUser *_Nullable user, NSError *_Nulla
6667
*/
6768
- (FIREmailEntryViewController *)emailEntryViewControllerForAuthUI:(FIRAuthUI *)authUI;
6869

70+
/** @fn passwordSignInViewControllerForAuthUI:email:
71+
@brief Sent to the receiver to ask for an instance of @c FIRPasswordSignInViewController subclass
72+
to allow sign-in UI customizations.
73+
@param authUI The @c FIRAuthUI instance sending the message.
74+
@param email The email user is using for sin-in.
75+
@return an instance of @c FIRPasswordSignInViewController subclass.
76+
*/
6977
- (FIRPasswordSignInViewController *)passwordSignInViewControllerForAuthUI:(FIRAuthUI *)authUI
7078
email:(NSString *)email;
7179

80+
/** @fn passwordSignInViewControllerForAuthUI:email:
81+
@brief Sent to the receiver to ask for an instance of @c FIRPasswordSignUpViewController subclass
82+
to allow sign-up UI customizations.
83+
@param authUI The @c FIRAuthUI instance sending the message.
84+
@param email The email user is using for sin-in.
85+
@return an instance of @c FIRPasswordSignUpViewController subclass.
86+
*/
87+
- (FIRPasswordSignUpViewController *)passwordSignUpViewControllerForAuthUI:(FIRAuthUI *)authUI
88+
email:(NSString *)email;
89+
7290
@end
7391

7492
/** @class FIRAuthUI

FirebaseAuthUI/FIREmailEntryViewController.m

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,16 @@ - (void)onNext:(NSString *)emailText {
148148
[self showAlertWithMessage:[FIRAuthUIStrings cannotAuthenticateError]];
149149
} else {
150150
// New user.
151-
UIViewController *controller =
152-
[[FIRPasswordSignUpViewController alloc] initWithAuthUI:self.authUI
153-
email:emailText];
151+
UIViewController *controller;
152+
if ([self.authUI.delegate respondsToSelector:@selector(passwordSignUpViewControllerForAuthUI:email:)]) {
153+
controller = [self.authUI.delegate passwordSignUpViewControllerForAuthUI:self.authUI
154+
email:emailText];
155+
} else {
156+
controller = [[FIRPasswordSignUpViewController alloc] initWithNibName:NSStringFromClass([FIRPasswordSignUpViewController class])
157+
bundle:[FIRAuthUIUtils frameworkBundle]
158+
authUI:self.authUI
159+
email:emailText];
160+
}
154161
[self pushViewController:controller];
155162
}
156163
}

FirebaseAuthUI/FIRPasswordSignUpViewController.h

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ NS_ASSUME_NONNULL_BEGIN
2424
@brief The view controller where user signs up as a password account.
2525
*/
2626
@interface FIRPasswordSignUpViewController : FIRAuthUIBaseViewController
27+
{
28+
@protected
29+
/** @var _email
30+
@brief The @c email address of the user from the previous screen.
31+
*/
32+
NSString *_email;
33+
}
2734

2835
/** @property footerTextView
2936
@brief The text view in the footer of the table.
@@ -42,13 +49,39 @@ NS_ASSUME_NONNULL_BEGIN
4249
*/
4350
- (instancetype)initWithAuthUI:(FIRAuthUI *)authUI NS_UNAVAILABLE;
4451

45-
/** @fn initWithAuthUI:email:
52+
/** @fn initWithNibName:bundle:authUI:email:
4653
@brief Designated initializer.
54+
@param nibNameOrNil The name of the nib file to associate with the view controller.
55+
@param nibBundleOrNil The bundle in which to search for the nib file.
4756
@param authUI The @c FIRAuthUI instance that manages this view controller.
4857
@param email The email address of the user.
4958
*/
50-
- (instancetype)initWithAuthUI:(FIRAuthUI *)authUI
51-
email:(NSString *_Nullable)email NS_DESIGNATED_INITIALIZER;
59+
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
60+
bundle:(nullable NSBundle *)nibBundleOrNil
61+
authUI:(FIRAuthUI *)authUI
62+
email:(NSString *_Nullable)email NS_DESIGNATED_INITIALIZER;
63+
64+
/** @fn didChangeEmail:orPassword:orUserName:
65+
@brief Should be called after any change of email, password or user name value.
66+
Updates UI controls state (e g state of next button)
67+
@param email The email address of the user.
68+
@param password The password which user uses.
69+
@param username The username which user uses.
70+
*/
71+
- (void)didChangeEmail:(NSString *)email
72+
orPassword:(NSString *)password
73+
orUserName:(NSString *)username;
74+
75+
/** @fn signUpWithEmail:andPassword:andUsername:
76+
@brief Should be called when user entered credentials and name. Sends request to create
77+
new user and second request to update it's name
78+
@param email The email address of the user.
79+
@param password The password which user uses.
80+
@param username The username which user uses.
81+
*/
82+
- (void)signUpWithEmail:(NSString *)email
83+
andPassword:(NSString *)password
84+
andUsername:(NSString *)username;
5285

5386
@end
5487

FirebaseAuthUI/FIRPasswordSignUpViewController.m

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,6 @@ @interface FIRPasswordSignUpViewController () <UITableViewDataSource, UITextFiel
6161
@end
6262

6363
@implementation FIRPasswordSignUpViewController {
64-
/** @var _email
65-
@brief The @c The email address of the user from the previous screen.
66-
*/
67-
NSString *_email;
68-
6964
/** @var _emailField
7065
@brief The @c UITextField that user enters email address into.
7166
*/
@@ -82,10 +77,12 @@ @implementation FIRPasswordSignUpViewController {
8277
UITextField *_passwordField;
8378
}
8479

85-
- (instancetype)initWithAuthUI:(FIRAuthUI *)authUI
86-
email:(NSString *_Nullable)email {
87-
self = [super initWithNibName:NSStringFromClass([self class])
88-
bundle:[FIRAuthUIUtils frameworkBundle]
80+
- (instancetype)initWithNibName:(nullable NSString *)nibNameOrNil
81+
bundle:(nullable NSBundle *)nibBundleOrNil
82+
authUI:(FIRAuthUI *)authUI
83+
email:(NSString *_Nullable)email {
84+
self = [super initWithNibName:nibNameOrNil
85+
bundle:nibBundleOrNil
8986
authUI:authUI];
9087
if (self) {
9188
_email = [email copy];
@@ -138,10 +135,18 @@ - (void)viewDidLayoutSubviews {
138135
#pragma mark - Actions
139136

140137
- (void)save {
138+
[self signUpWithEmail:_emailField.text
139+
andPassword:_passwordField.text
140+
andUsername:_nameField.text];
141+
}
142+
143+
- (void)signUpWithEmail:(NSString *)email
144+
andPassword:(NSString *)password
145+
andUsername:(NSString *)username {
141146
[self incrementActivity];
142147

143-
[self.auth createUserWithEmail:_emailField.text
144-
password:_passwordField.text
148+
[self.auth createUserWithEmail:email
149+
password:password
145150
completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
146151
if (error) {
147152
[self decrementActivity];
@@ -151,7 +156,7 @@ - (void)save {
151156
}
152157

153158
FIRUserProfileChangeRequest *request = [user profileChangeRequest];
154-
request.displayName = _nameField.text;
159+
request.displayName = username;
155160
[request commitChangesWithCompletion:^(NSError *_Nullable error) {
156161
[self decrementActivity];
157162

@@ -188,13 +193,16 @@ - (void)finishSignUpWithUser:(FIRUser *)user error:(NSError *)error {
188193
}
189194

190195
- (void)textFieldDidChange {
191-
[self updateActionButton];
196+
[self didChangeEmail:_emailField.text orPassword:_nameField.text orUserName:_passwordField.text];
192197
}
193198

194-
- (void)updateActionButton {
195-
BOOL enableActionButton = _emailField.text.length > 0
196-
&& _nameField.text.length > 0
197-
&& _passwordField.text.length > 0;
199+
- (void)didChangeEmail:(NSString *)email
200+
orPassword:(NSString *)password
201+
orUserName:(NSString *)username {
202+
203+
BOOL enableActionButton = email.length > 0
204+
&& password.length > 0
205+
&& username.length > 0;
198206
self.navigationItem.rightBarButtonItem.enabled = enableActionButton;
199207
}
200208

@@ -245,7 +253,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView
245253
[cell.textField addTarget:self
246254
action:@selector(textFieldDidChange)
247255
forControlEvents:UIControlEventEditingChanged];
248-
[self updateActionButton];
256+
[self didChangeEmail:_emailField.text orPassword:_nameField.text orUserName:_passwordField.text];
249257
return cell;
250258
}
251259

@@ -257,7 +265,9 @@ - (BOOL)textFieldShouldReturn:(UITextField *)textField {
257265
} else if (textField == _nameField) {
258266
[_passwordField becomeFirstResponder];
259267
} else if (textField == _passwordField) {
260-
[self save];
268+
[self signUpWithEmail:_emailField.text
269+
andPassword:_passwordField.text
270+
andUsername:_nameField.text];
261271
}
262272
return NO;
263273
}

samples/objc/FirebaseUI-demo-objc.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
C375CB181D935D1F002EECD3 /* FIRCustomAuthPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C375CB161D935D1F002EECD3 /* FIRCustomAuthPickerViewController.m */; };
1616
C375CB191D935D1F002EECD3 /* FIRCustomAuthPickerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C375CB171D935D1F002EECD3 /* FIRCustomAuthPickerViewController.xib */; };
1717
C375CB1C1D943D5F002EECD3 /* FIRCustomAuthUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C375CB1B1D943D5F002EECD3 /* FIRCustomAuthUIDelegate.m */; };
18+
C3A8B7C11DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A8B7BF1DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.m */; };
19+
C3A8B7C21DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3A8B7C01DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.xib */; };
20+
C3A8B7C61DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A8B7C41DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.m */; };
21+
C3A8B7C71DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C3A8B7C51DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.xib */; };
1822
C3AC675E1D81FE6B00FC956D /* FIRAuthViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AC67531D81FE6B00FC956D /* FIRAuthViewController.m */; };
1923
C3AC675F1D81FE6B00FC956D /* FIRChatMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AC67561D81FE6B00FC956D /* FIRChatMessage.m */; };
2024
C3AC67601D81FE6B00FC956D /* FIRChatMessageDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = C3AC67581D81FE6B00FC956D /* FIRChatMessageDataSource.m */; };
@@ -45,6 +49,12 @@
4549
C375CB171D935D1F002EECD3 /* FIRCustomAuthPickerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FIRCustomAuthPickerViewController.xib; sourceTree = "<group>"; };
4650
C375CB1A1D943D5F002EECD3 /* FIRCustomAuthUIDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRCustomAuthUIDelegate.h; sourceTree = "<group>"; };
4751
C375CB1B1D943D5F002EECD3 /* FIRCustomAuthUIDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRCustomAuthUIDelegate.m; sourceTree = "<group>"; };
52+
C3A8B7BE1DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRCustomPasswordSignInViewController.h; sourceTree = "<group>"; };
53+
C3A8B7BF1DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRCustomPasswordSignInViewController.m; sourceTree = "<group>"; };
54+
C3A8B7C01DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FIRCustomPasswordSignInViewController.xib; sourceTree = "<group>"; };
55+
C3A8B7C31DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRCustomPasswordSignUpViewController.h; sourceTree = "<group>"; };
56+
C3A8B7C41DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRCustomPasswordSignUpViewController.m; sourceTree = "<group>"; };
57+
C3A8B7C51DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = FIRCustomPasswordSignUpViewController.xib; sourceTree = "<group>"; };
4858
C3AC67521D81FE6B00FC956D /* FIRAuthViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRAuthViewController.h; sourceTree = "<group>"; };
4959
C3AC67531D81FE6B00FC956D /* FIRAuthViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRAuthViewController.m; sourceTree = "<group>"; };
5060
C3AC67551D81FE6B00FC956D /* FIRChatMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRChatMessage.h; sourceTree = "<group>"; };
@@ -131,6 +141,12 @@
131141
C329B1AF1DAD6E5100059A13 /* FIRCustomEmailEntryViewController.h */,
132142
C329B1B01DAD6E5100059A13 /* FIRCustomEmailEntryViewController.m */,
133143
C329B1B11DAD6E5100059A13 /* FIRCustomEmailEntryViewController.xib */,
144+
C3A8B7BE1DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.h */,
145+
C3A8B7BF1DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.m */,
146+
C3A8B7C01DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.xib */,
147+
C3A8B7C31DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.h */,
148+
C3A8B7C41DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.m */,
149+
C3A8B7C51DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.xib */,
134150
);
135151
path = Auth;
136152
sourceTree = "<group>";
@@ -257,11 +273,13 @@
257273
isa = PBXResourcesBuildPhase;
258274
buildActionMask = 2147483647;
259275
files = (
276+
C3A8B7C21DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.xib in Resources */,
260277
D81A05FF1B86A78700498183 /* Main.storyboard in Resources */,
261278
C3AC67621D81FE6B00FC956D /* FIRChatMessageTableViewCell.xib in Resources */,
262279
D81A06041B86A78700498183 /* LaunchScreen.xib in Resources */,
263280
D81A06011B86A78700498183 /* Images.xcassets in Resources */,
264281
C329B1B31DAD6E5100059A13 /* FIRCustomEmailEntryViewController.xib in Resources */,
282+
C3A8B7C71DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.xib in Resources */,
265283
C375CB191D935D1F002EECD3 /* FIRCustomAuthPickerViewController.xib in Resources */,
266284
8D7D5DC11D9D9536006C1857 /* GoogleService-Info.plist in Resources */,
267285
);
@@ -324,11 +342,13 @@
324342
files = (
325343
D81A05F91B86A78700498183 /* AppDelegate.m in Sources */,
326344
C375CB181D935D1F002EECD3 /* FIRCustomAuthPickerViewController.m in Sources */,
345+
C3A8B7C11DAF073400CDF0ED /* FIRCustomPasswordSignInViewController.m in Sources */,
327346
C375CB1C1D943D5F002EECD3 /* FIRCustomAuthUIDelegate.m in Sources */,
328347
C3F23ED01D80F58A0020509F /* FIRSamplesViewController.m in Sources */,
329348
C3AC67631D81FE6B00FC956D /* FIRChatViewController.m in Sources */,
330349
C3AC67611D81FE6B00FC956D /* FIRChatMessageTableViewCell.m in Sources */,
331350
C3AC67601D81FE6B00FC956D /* FIRChatMessageDataSource.m in Sources */,
351+
C3A8B7C61DB027D200CDF0ED /* FIRCustomPasswordSignUpViewController.m in Sources */,
332352
C3AC675F1D81FE6B00FC956D /* FIRChatMessage.m in Sources */,
333353
D81A05F61B86A78700498183 /* main.m in Sources */,
334354
C3AC67661D82002F00FC956D /* FIRSample.m in Sources */,

samples/objc/FirebaseUI-demo-objc/Samples/Auth/FIRCustomAuthPickerViewController.xib

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
<dependencies>
44
<deployment identifier="iOS"/>
55
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11161"/>
6+
<capability name="Alignment constraints to the first baseline" minToolsVersion="6.0"/>
7+
<capability name="Alignment constraints with different attributes" minToolsVersion="5.1"/>
68
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
79
</dependencies>
810
<objects>
@@ -16,14 +18,7 @@
1618
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
1719
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
1820
<subviews>
19-
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Custom Picker View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xJ4-3A-3tu">
20-
<fontDescription key="fontDescription" type="system" pointSize="17"/>
21-
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
22-
<nil key="highlightedColor"/>
23-
</label>
24-
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XdB-tD-X9o">
25-
<frame key="frameInset" minX="312" minY="45" width="30" height="30"/>
26-
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
21+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="XdB-tD-X9o">
2722
<color key="backgroundColor" name="selectedKnobColor" catalog="System" colorSpace="catalog"/>
2823
<state key="normal" title="X"/>
2924
<userDefinedRuntimeAttributes>
@@ -35,11 +30,21 @@
3530
<action selector="onClose:" destination="-1" eventType="touchUpInside" id="835-nK-Off"/>
3631
</connections>
3732
</button>
33+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Custom Picker View" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xJ4-3A-3tu">
34+
<fontDescription key="fontDescription" type="system" pointSize="17"/>
35+
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
36+
<nil key="highlightedColor"/>
37+
</label>
3838
</subviews>
3939
<color key="backgroundColor" name="knobColor" catalog="System" colorSpace="catalog"/>
4040
<constraints>
41+
<constraint firstItem="XdB-tD-X9o" firstAttribute="baseline" secondItem="xJ4-3A-3tu" secondAttribute="firstBaseline" id="QGX-WU-m5B"/>
42+
<constraint firstItem="XdB-tD-X9o" firstAttribute="top" secondItem="feM-QA-xVH" secondAttribute="top" constant="45" id="a3W-X7-uh5"/>
43+
<constraint firstItem="XdB-tD-X9o" firstAttribute="firstBaseline" secondItem="xJ4-3A-3tu" secondAttribute="baseline" id="aeU-fW-NxF"/>
44+
<constraint firstAttribute="trailing" secondItem="XdB-tD-X9o" secondAttribute="trailing" constant="33" id="gjx-4V-Cgx"/>
45+
<constraint firstItem="XdB-tD-X9o" firstAttribute="baseline" secondItem="xJ4-3A-3tu" secondAttribute="baseline" id="mLu-56-aHx"/>
4146
<constraint firstItem="xJ4-3A-3tu" firstAttribute="centerX" secondItem="feM-QA-xVH" secondAttribute="centerX" id="obp-jt-NjF"/>
42-
<constraint firstItem="xJ4-3A-3tu" firstAttribute="top" secondItem="feM-QA-xVH" secondAttribute="top" constant="127" id="ypO-V8-Owu"/>
47+
<constraint firstItem="xJ4-3A-3tu" firstAttribute="top" secondItem="feM-QA-xVH" secondAttribute="top" constant="50" id="ypO-V8-Owu"/>
4348
</constraints>
4449
<userDefinedRuntimeAttributes>
4550
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">

0 commit comments

Comments
 (0)