Skip to content

Commit

Permalink
share shared model's c++ header (#1445)
Browse files Browse the repository at this point in the history
* phase 1: share all header complete

* updated xcode project files

* dev complete

* CR fix

* fixed CR update
  • Loading branch information
jwoo-msft authored and Gilles Khouzam committed May 21, 2018
1 parent 42e0271 commit e941b49
Show file tree
Hide file tree
Showing 12 changed files with 386 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
objects = {

/* Begin PBXBuildFile section */
6BF339D620A665E600DA5973 /* CustomTextBlockRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */; };
6BF339E320A66A3F00DA5973 /* AdaptiveCards.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */; };
6BF339E420A66A4D00DA5973 /* AdaptiveCards.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
F4071C831FD63D5400AF4FEA /* Solitaire.json in Resources */ = {isa = PBXBuildFile; fileRef = F4071C821FD63D5300AF4FEA /* Solitaire.json */; };
F423C0771EE1FB6100905679 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F423C0761EE1FB6100905679 /* main.m */; };
F423C07A1EE1FB6100905679 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F423C0791EE1FB6100905679 /* AppDelegate.m */; };
Expand Down Expand Up @@ -66,8 +69,6 @@
F4933D061F79853B00F6EBFD /* StockUpdate.json in Resources */ = {isa = PBXBuildFile; fileRef = F4933CF91F79853B00F6EBFD /* StockUpdate.json */; };
F4933D071F79853B00F6EBFD /* WeatherCompact.json in Resources */ = {isa = PBXBuildFile; fileRef = F4933CFA1F79853B00F6EBFD /* WeatherCompact.json */; };
F4933D081F79853B00F6EBFD /* WeatherLarge.json in Resources */ = {isa = PBXBuildFile; fileRef = F4933CFB1F79853B00F6EBFD /* WeatherLarge.json */; };
F49CECC01F62412E00D7FB55 /* AdaptiveCards.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F49CECBF1F62412E00D7FB55 /* AdaptiveCards.framework */; };
F49CECC11F62412E00D7FB55 /* AdaptiveCards.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F49CECBF1F62412E00D7FB55 /* AdaptiveCards.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
F4D33E861F04705800941E44 /* ACVTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F4D33E851F04705800941E44 /* ACVTableViewController.m */; };
F4D402161F7DB0BF00D0356B /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = F4D402151F7DB0BF00D0356B /* sample.json */; };
F4F255721F993CD200A80D39 /* CustomActionOpenURLRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4F255711F993CD200A80D39 /* CustomActionOpenURLRenderer.mm */; };
Expand Down Expand Up @@ -102,14 +103,17 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
F49CECC11F62412E00D7FB55 /* AdaptiveCards.framework in Embed Frameworks */,
6BF339E420A66A4D00DA5973 /* AdaptiveCards.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CustomTextBlockRenderer.mm; sourceTree = "<group>"; };
6BF339D520A665E600DA5973 /* CustomTextBlockRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTextBlockRenderer.h; sourceTree = "<group>"; };
6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AdaptiveCards.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F4071C821FD63D5300AF4FEA /* Solitaire.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Solitaire.json; path = ../../../../samples/v1.0/Scenarios/Solitaire.json; sourceTree = "<group>"; };
F423C0721EE1FB6100905679 /* ADCIOSVisualizer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ADCIOSVisualizer.app; sourceTree = BUILT_PRODUCTS_DIR; };
F423C0761EE1FB6100905679 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -197,7 +201,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F49CECC01F62412E00D7FB55 /* AdaptiveCards.framework in Frameworks */,
6BF339E320A66A3F00DA5973 /* AdaptiveCards.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -218,6 +222,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
6BF339E120A66A3F00DA5973 /* Frameworks */ = {
isa = PBXGroup;
children = (
6BF339E220A66A3F00DA5973 /* AdaptiveCards.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F423C0691EE1FB6100905679 = {
isa = PBXGroup;
children = (
Expand All @@ -227,6 +239,7 @@
F423C0911EE1FB6100905679 /* ADCIOSVisualizerTests */,
F423C09C1EE1FB6100905679 /* ADCIOSVisualizerUITests */,
F423C0731EE1FB6100905679 /* Products */,
6BF339E120A66A3F00DA5973 /* Frameworks */,
);
sourceTree = "<group>";
};
Expand All @@ -243,6 +256,8 @@
F423C0741EE1FB6100905679 /* ADCIOSVisualizer */ = {
isa = PBXGroup;
children = (
6BF339D520A665E600DA5973 /* CustomTextBlockRenderer.h */,
6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */,
F4F44BA8204CF98900A2F24C /* CustomProgressBarRenderer.h */,
F4F44BA9204CF98900A2F24C /* CustomProgressBarRenderer.mm */,
F4F44B74203FE73D00A2F24C /* CustomInputNumberRenderer.h */,
Expand Down Expand Up @@ -415,7 +430,7 @@
F423C06A1EE1FB6100905679 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0900;
LastUpgradeCheck = 0930;
ORGANIZATIONNAME = Microsoft;
TargetAttributes = {
F423C0711EE1FB6100905679 = {
Expand Down Expand Up @@ -543,6 +558,7 @@
files = (
F4F44BAA204CF98900A2F24C /* CustomProgressBarRenderer.mm in Sources */,
F4D33E861F04705800941E44 /* ACVTableViewController.m in Sources */,
6BF339D620A665E600DA5973 /* CustomTextBlockRenderer.mm in Sources */,
F423C07D1EE1FB6100905679 /* ViewController.m in Sources */,
F423C07A1EE1FB6100905679 /* AppDelegate.m in Sources */,
F4F255721F993CD200A80D39 /* CustomActionOpenURLRenderer.mm in Sources */,
Expand Down Expand Up @@ -609,21 +625,23 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
Expand Down Expand Up @@ -663,21 +681,23 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0920"
LastUpgradeVersion = "0930"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand All @@ -26,7 +26,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
Expand Down Expand Up @@ -66,7 +65,6 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// CustomTextBlockRenderer.h
// ADCIOSVisualizer
//
// Copyright © 2018 Microsoft. All rights reserved.
//

#import <Foundation/Foundation.h>

#import <AdaptiveCards/ACFramework.h>

@interface CustomTextBlockRenderer:ACRBaseCardElementRenderer

+ (CustomTextBlockRenderer *)getInstance;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
//
// CustomTextBlockRenderer
// CustomTextBlockRenderer.mm
//
// Copyright © 2018 Microsoft. All rights reserved.
//

#import "CustomTextBlockRenderer.h"
#import <AdaptiveCards/ACFramework.h>
#import <AdaptiveCards/SharedAdaptiveCard.h>
#import <AdaptiveCards/TextBlock.h>
#import <AdaptiveCards/ACRTextBlockRenderer.h>
#import <AdaptiveCards/ACOBaseCardElementPrivate.h>
#import <AdaptiveCards/ACRContentHoldingUIView.h>
#import <AdaptiveCards/MarkDownParser.h>
#import <AdaptiveCards/HostConfig.h>
#import <AdaptiveCards/ACOHostConfigPrivate.h>

@implementation CustomTextBlockRenderer

+ (CustomTextBlockRenderer *)getInstance
{
static CustomTextBlockRenderer *singletonInstance = [[self alloc] init];
return singletonInstance;
}

- (UIView *)render:(UIView<ACRIContentHoldingView> *)viewGroup
rootView:(ACRView *)rootView
inputs:(NSArray *)inputs
baseCardElement:(ACOBaseCardElement *)acoElem
hostConfig:(ACOHostConfig *)acoConfig
{

std::shared_ptr<BaseCardElement> elem = [acoElem element];
std::shared_ptr<TextBlock> textBlockElement = std::static_pointer_cast<TextBlock>(elem);

struct TextConfig textConfigForBlock =
{
.weight = textBlockElement->GetTextWeight(),
.size = textBlockElement->GetTextSize(),
.color = textBlockElement->GetTextColor(),
.isSubtle = textBlockElement->GetIsSubtle(),
.wrap = textBlockElement->GetWrap()
};

std::string textForBlock = textBlockElement->GetText();

NSString* parsedString = nil;
// MarkDownParser transforms text with MarkDown to a html string
std::shared_ptr<MarkDownParser> markDownParser = std::make_shared<MarkDownParser>([ACOHostConfig getLocalizedDate:textBlockElement]);
parsedString = [NSString stringWithCString:markDownParser->TransformToHtml().c_str() encoding:NSUTF8StringEncoding];

// if correctly initialized, fonFamilyNames array is bigger than zero
NSMutableString *fontFamilyName = [[NSMutableString alloc] initWithString:@"'"];
[fontFamilyName appendString:[acoConfig.fontFamilyNames componentsJoinedByString:@"', '"]];
[fontFamilyName appendString:@"'"];

// Font and text size are applied as CSS style by appending it to the html string
parsedString = [parsedString stringByAppendingString:[NSString stringWithFormat:@"<style>body{font-family: %@; font-size:%dpx; font-weight: %d;}</style>",
fontFamilyName,
[acoConfig getTextBlockTextSize:textConfigForBlock.size],
[acoConfig getTextBlockFontWeight:textConfigForBlock.weight]]];
// Convert html string to NSMutableAttributedString, NSAttributedString knows how to apply html tags
NSData *htmlData = [parsedString dataUsingEncoding:NSUTF16StringEncoding];
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType};

// Initializing NSMutableAttributedString for HTML rendering is very slow
NSMutableAttributedString *content = [[NSMutableAttributedString alloc] initWithData:htmlData options:options documentAttributes:nil error:nil];

UILabel *lab = [[UILabel alloc] init]; // generate key for text map from TextBlock element's id
// Set paragraph style such as line break mode and alignment
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = textConfigForBlock.wrap ? NSLineBreakByWordWrapping:NSLineBreakByTruncatingTail;
paragraphStyle.alignment = [ACOHostConfig getTextBlockAlignment:textBlockElement->GetHorizontalAlignment()];

// Obtain text color to apply to the attributed string
ACRContainerStyle style = viewGroup.style;
ColorsConfig &colorConfig = (style == ACREmphasis)? [acoConfig getHostConfig]->containerStyles.emphasisPalette.foregroundColors:
[acoConfig getHostConfig]->containerStyles.defaultPalette.foregroundColors;
// Add paragraph style, text color, text weight as attributes to a NSMutableAttributedString, content.
[content addAttributes:@{NSParagraphStyleAttributeName:paragraphStyle, NSForegroundColorAttributeName:[ACOHostConfig getTextBlockColor:textConfigForBlock.color colorsConfig:colorConfig subtleOption:textConfigForBlock.isSubtle],} range:NSMakeRange(0, content.length)];
lab.attributedText = content;

lab.numberOfLines = int(textBlockElement->GetMaxLines());
if(!lab.numberOfLines && !textBlockElement->GetWrap()){
lab.numberOfLines = 1;
}

ACRContentHoldingUIView *wrappingview = [[ACRContentHoldingUIView alloc] init];

[wrappingview addSubview:lab];

[wrappingview setAlignmentForSubview:textBlockElement->GetHorizontalAlignment()];

[viewGroup addArrangedSubview:wrappingview];

wrappingview.translatesAutoresizingMaskIntoConstraints = false;

wrappingview.backgroundColor = UIColor.lightGrayColor;

lab.translatesAutoresizingMaskIntoConstraints = false;

return wrappingview;
}
@end
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
@property (strong, nonatomic) UIStackView *buttonLayout;
@property (strong, nonatomic) UIButton *tryButton;
@property (strong, nonatomic) UIButton *applyButton;
@property (strong, nonatomic) UIButton *enableCustomRendererButton;
@property (strong, nonatomic) NSString *editableStr;
@property (strong, nonatomic) NSString *hostconfig;
@property UIView *curView;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@
#import "CustomActionOpenURLRenderer.h"
#import "CustomInputNumberRenderer.h"
#import "CustomProgressBarRenderer.h"
#import "CustomTextBlockRenderer.h"

@interface ViewController ()
{
BOOL _enableCustomRenderer;
}

@end

Expand Down Expand Up @@ -56,6 +60,23 @@ - (IBAction)editText:(id)sender
@"V:|-40-[editView(==200)]-[buttonLayout]", nil];
[ViewController applyConstraints:formats variables:viewMap];
}
- (IBAction)toggleCustomRenderer:(id)sender
{
_enableCustomRenderer = !_enableCustomRenderer;
ACRRegistration *registration = [ACRRegistration getInstance];
if(_enableCustomRenderer){
// enum will be part of API in next iterations when custom renderer extended to non-action type - tracked by issue #809
[registration setActionRenderer:[CustomActionOpenURLRenderer getInstance] cardElementType:@3];
[registration setBaseCardElementRenderer:[CustomTextBlockRenderer getInstance] cardElementType:ACRTextBlock];
[registration setBaseCardElementRenderer:[CustomInputNumberRenderer getInstance] cardElementType:ACRNumberInput];
} else
{
[registration setActionRenderer:nil cardElementType:@3];
[registration setBaseCardElementRenderer:nil cardElementType:ACRTextBlock];
[registration setBaseCardElementRenderer:nil cardElementType:ACRNumberInput];
}
[self update:self.editableStr];
}

- (IBAction)applyText:(id)sender
{
Expand All @@ -75,6 +96,7 @@ - (IBAction)applyText:(id)sender
- (void)viewDidLoad {
[super viewDidLoad];
[self registerForKeyboardNotifications];
_enableCustomRenderer = NO;
self.curView = nil;
self.ACVTabVC = [[ACVTableViewController alloc] init];
self.ACVTabVC.delegate = self;
Expand Down Expand Up @@ -124,10 +146,25 @@ - (void)viewDidLoad {
[self.applyButton addTarget:self action:@selector(applyText:)
forControlEvents:UIControlEventTouchUpInside];
[buttonLayout addArrangedSubview:self.applyButton];

// custon renderer button
self.enableCustomRendererButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.enableCustomRendererButton setTitle:@"Enable Custom Renderer" forState:UIControlStateNormal];
[self.enableCustomRendererButton setTitleColor:[UIColor colorWithRed:0/255 green:122.0/255 blue:1 alpha:1] forState:UIControlStateSelected];
[self.enableCustomRendererButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];

self.enableCustomRendererButton.backgroundColor = [UIColor colorWithRed:0/255 green:122.0/255 blue:1 alpha:1];
self.enableCustomRendererButton.contentEdgeInsets = UIEdgeInsetsMake(5,5,5,5);
[NSLayoutConstraint constraintWithItem:_enableCustomRendererButton attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:25].active = YES;

[self.enableCustomRendererButton addTarget:self action:@selector(toggleCustomRenderer:)
forControlEvents:UIControlEventTouchUpInside];
[buttonLayout addArrangedSubview:self.enableCustomRendererButton];

[self.view addSubview:buttonLayout];
buttonLayout.translatesAutoresizingMaskIntoConstraints = NO;
buttonLayout.alignment = UIStackViewAlignmentCenter;
buttonLayout.distribution = UIStackViewDistributionFillEqually;
buttonLayout.distribution = UIStackViewDistributionFillProportionally;
buttonLayout.spacing = 10;

[NSLayoutConstraint constraintWithItem:buttonLayout attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:30].active = YES;
Expand Down Expand Up @@ -165,18 +202,16 @@ - (void)update:(NSString *) jsonStr
ACOHostConfigParseResult *hostconfigParseResult = [ACOHostConfig fromJson:self.hostconfig];
ACOAdaptiveCardParseResult *cardParseResult = [ACOAdaptiveCard fromJson:jsonStr];
if(cardParseResult.isValid){
ACRRegistration *registration = [ACRRegistration getInstance];

CustomProgressBarRenderer *progressBarRenderer = [[CustomProgressBarRenderer alloc] init];
[registration setCustomElementParser:progressBarRenderer];

renderResult = [ACRRenderer render:cardParseResult.card config:hostconfigParseResult.config widthConstraint:300];
}

if(renderResult.succeeded)
{
ACRRegistration *registration = [ACRRegistration getInstance];
// enum will be part of API in next iterations when custom renderer extended to non-action type - tracked by issue #809
[registration setActionRenderer:[CustomActionOpenURLRenderer getInstance] cardElementType:@3];
[registration setBaseCardElementRenderer:[CustomInputNumberRenderer getInstance] cardElementType:ACRNumberInput];

CustomProgressBarRenderer *progressBarRenderer = [[CustomProgressBarRenderer alloc] init];
[registration setCustomElementParser:progressBarRenderer];
ACRView *ad = renderResult.view;
ad.acrActionDelegate = self;
if(self.curView)
Expand Down

0 comments on commit e941b49

Please sign in to comment.