Skip to content

Commit

Permalink
Jwoo/ios custom background image loader (#1642)
Browse files Browse the repository at this point in the history
* fixes url loading sync issue for actions

removed hard coded image icon placement

* allows custom BG IMG renderer

* Added CustomImageRenderer to sample

* removed CoreGraphics framework as it's not used
  • Loading branch information
jwoo-msft committed Jun 27, 2018
1 parent c4c4aa9 commit a61c17e
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
6B268FE320CEF19400D99C1B /* Action.Scrolling.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B268FE220CEF19400D99C1B /* Action.Scrolling.json */; };
6B268FE520CEF89100D99C1B /* Action.OpenUrl.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B268FE420CEF89100D99C1B /* Action.OpenUrl.json */; };
6B7B1A9B20C21CA900260731 /* SportingEvent.json in Resources */ = {isa = PBXBuildFile; fileRef = 6B7B1A9920C21CA800260731 /* SportingEvent.json */; };
6B9BDF7020E18E5B00F13155 /* CustomImageRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B9BDF6F20E18E5B00F13155 /* CustomImageRenderer.mm */; };
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, ); }; };
Expand Down Expand Up @@ -116,6 +117,8 @@
6B268FE220CEF19400D99C1B /* Action.Scrolling.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Action.Scrolling.json; path = resources/Action.Scrolling.json; sourceTree = "<group>"; };
6B268FE420CEF89100D99C1B /* Action.OpenUrl.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Action.OpenUrl.json; path = ../../../../samples/v1.0/Elements/Action.OpenUrl.json; sourceTree = "<group>"; };
6B7B1A9920C21CA800260731 /* SportingEvent.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = SportingEvent.json; path = ../../../../samples/v1.0/Scenarios/SportingEvent.json; sourceTree = "<group>"; };
6B9BDF6E20E18E5B00F13155 /* CustomImageRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomImageRenderer.h; sourceTree = "<group>"; };
6B9BDF6F20E18E5B00F13155 /* CustomImageRenderer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CustomImageRenderer.mm; sourceTree = "<group>"; };
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; };
Expand Down Expand Up @@ -260,6 +263,8 @@
F423C0741EE1FB6100905679 /* ADCIOSVisualizer */ = {
isa = PBXGroup;
children = (
6B9BDF6E20E18E5B00F13155 /* CustomImageRenderer.h */,
6B9BDF6F20E18E5B00F13155 /* CustomImageRenderer.mm */,
6BF339D520A665E600DA5973 /* CustomTextBlockRenderer.h */,
6BF339D420A665E600DA5973 /* CustomTextBlockRenderer.mm */,
F4F44BA8204CF98900A2F24C /* CustomProgressBarRenderer.h */,
Expand Down Expand Up @@ -565,6 +570,7 @@
F4F44BAA204CF98900A2F24C /* CustomProgressBarRenderer.mm in Sources */,
F4D33E861F04705800941E44 /* ACVTableViewController.m in Sources */,
6BF339D620A665E600DA5973 /* CustomTextBlockRenderer.mm in Sources */,
6B9BDF7020E18E5B00F13155 /* CustomImageRenderer.mm in Sources */,
F423C07D1EE1FB6100905679 /* ViewController.m in Sources */,
F423C07A1EE1FB6100905679 /* AppDelegate.m in Sources */,
F4F255721F993CD200A80D39 /* CustomActionOpenURLRenderer.mm in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// CustomImageRenderer.h
// ADCIOSVisualizer
//
// Copyright © 2018 Microsoft. All rights reserved.
//

#import <Foundation/Foundation.h>

#import <AdaptiveCards/ACFramework.h>

@interface CustomImageRenderer:ACRBaseCardElementRenderer

+ (CustomImageRenderer *)getInstance;

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

#import "CustomImageRenderer.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>
#import <AdaptiveCards/ACRUIImageView.h>
#import <AdaptiveCards/Image.h>
#import <AdaptiveCards/ACRLongPressGestureRecognizerFactory.h>

@implementation CustomImageRenderer

+ (CustomImageRenderer *)getInstance
{
static CustomImageRenderer *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<Image> imgElem = std::dynamic_pointer_cast<Image>(elem);
ACRUIImageView *view;
NSInteger pixelWidth = imgElem->GetPixelWidth(), pixelHeight = imgElem->GetPixelHeight();
BOOL hasExplicitMeasurements = (pixelWidth || pixelHeight);
BOOL isAspectRatioNeeded = !(pixelWidth && pixelHeight);
CGSize cgsize = [acoConfig getImageSize:imgElem->GetImageSize()];

NSString *urlStr = [NSString stringWithCString:imgElem->GetUrl().c_str()
encoding:[NSString defaultCStringEncoding]];
NSURL *nsurl = [NSURL URLWithString:urlStr];
// download image
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:nsurl]];

CGFloat heightToWidthRatio = 0.0f, widthToHeightRatio = 0.0f;
if(img){
if(img.size.width > 0) {
heightToWidthRatio = img.size.height / img.size.width;
}

if(img.size.height > 0) {
widthToHeightRatio =img.size.width / img.size.height;
}
}

if(hasExplicitMeasurements) {
if(pixelWidth){
cgsize.width = pixelWidth;
if(isAspectRatioNeeded) {
cgsize.height = pixelWidth * heightToWidthRatio;
}
}
if(pixelHeight){
cgsize.height = pixelHeight;
if(isAspectRatioNeeded) {
cgsize.width = pixelHeight * widthToHeightRatio;
}
}
}
view = [[ACRUIImageView alloc] initWithFrame:CGRectMake(0, 0, cgsize.width, cgsize.height)];
view.image = img;

ImageSize size = ImageSize::None;
if (!hasExplicitMeasurements){
size = imgElem->GetImageSize();
if (size == ImageSize::None) {
size = [acoConfig getHostConfig]->image.imageSize;
}
}

if(size != ImageSize::Auto && size != ImageSize::Stretch){
[view addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:cgsize.width],
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:cgsize.height]]];
}

if(heightToWidthRatio && widthToHeightRatio && (size == ImageSize::Auto || size == ImageSize::Stretch)){
[view addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeWidth
multiplier:heightToWidthRatio
constant:0]]];
[view addConstraints:@[[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeHeight
multiplier:widthToHeightRatio
constant:0]]];
}

if(!isAspectRatioNeeded){
view.contentMode = UIViewContentModeScaleToFill;
} else {
view.contentMode = UIViewContentModeScaleAspectFit;
}
view.clipsToBounds = NO;
if(imgElem->GetImageStyle() == ImageStyle::Person) {
CALayer *imgLayer = view.layer;
[imgLayer setCornerRadius:cgsize.width/2];
[imgLayer setMasksToBounds:YES];
}

ACRContentHoldingUIView *wrappingview = [[ACRContentHoldingUIView alloc] initWithFrame:view.frame];
[wrappingview addSubview:view];

[viewGroup addArrangedSubview:wrappingview];

[wrappingview addConstraints:[ACOHostConfig getConstraintsForImageAlignment:imgElem->GetHorizontalAlignment()
withSuperview:wrappingview
toView:view]];
// ImageSize::Auto should maintain its intrinsic size
if(size == ImageSize::Auto || size == ImageSize::Stretch) {
NSArray<NSString *> *visualFormats = [NSArray arrayWithObjects:@"H:[view(<=wrappingview)]", @"V:|-[view(<=wrappingview)]-|", nil];
NSDictionary *viewMap = NSDictionaryOfVariableBindings(view, wrappingview);
for(NSString *constraint in visualFormats){
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constraint options:0 metrics:nil views:viewMap]];
}
} else {
[wrappingview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[wrappingview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
}

if(size == ImageSize::Auto) {
[view setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[view setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[wrappingview setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[wrappingview setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];

if(imgElem->GetHeight() == HeightType::Stretch) {
UIView *blankTrailingSpace = [[UIView alloc] init];
[blankTrailingSpace setContentHuggingPriority:(UILayoutPriorityDefaultLow) forAxis:UILayoutConstraintAxisVertical];
[viewGroup addArrangedSubview:blankTrailingSpace];
}
}
std::shared_ptr<BaseActionElement> selectAction = imgElem->GetSelectAction();
// instantiate and add tap gesture recognizer
UILongPressGestureRecognizer * gestureRecognizer =
[ACRLongPressGestureRecognizerFactory getLongPressGestureRecognizer:viewGroup
rootView:rootView
targetView:wrappingview
actionElement:selectAction
inputs:inputs
hostConfig:acoConfig];
if(gestureRecognizer) {
[view addGestureRecognizer:gestureRecognizer];
view.userInteractionEnabled = YES;
}
view.translatesAutoresizingMaskIntoConstraints = NO;
wrappingview.translatesAutoresizingMaskIntoConstraints = NO;
return wrappingview;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ - (UIView *)render:(UIView<ACRIContentHoldingView> *)viewGroup
}

[viewGroup addArrangedSubview:lab];

lab.backgroundColor = UIColor.lightGrayColor;

lab.translatesAutoresizingMaskIntoConstraints = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "CustomInputNumberRenderer.h"
#import "CustomProgressBarRenderer.h"
#import "CustomTextBlockRenderer.h"
#import "CustomImageRenderer.h"

@interface ViewController ()
{
Expand Down Expand Up @@ -69,11 +70,13 @@ - (IBAction)toggleCustomRenderer:(id)sender
[registration setActionRenderer:[CustomActionOpenURLRenderer getInstance] cardElementType:@3];
[registration setBaseCardElementRenderer:[CustomTextBlockRenderer getInstance] cardElementType:ACRTextBlock];
[registration setBaseCardElementRenderer:[CustomInputNumberRenderer getInstance] cardElementType:ACRNumberInput];
[registration setBaseCardElementRenderer:[CustomImageRenderer getInstance] cardElementType:ACRImage];
} else
{
[registration setActionRenderer:nil cardElementType:@3];
[registration setBaseCardElementRenderer:nil cardElementType:ACRTextBlock];
[registration setBaseCardElementRenderer:nil cardElementType:ACRNumberInput];
[registration setBaseCardElementRenderer:nil cardElementType:ACRImage];
}
[self update:self.editableStr];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -526,8 +526,8 @@
F4F2556B1F98246000A80D39 /* ACOBaseActionElement.h */,
F4F44B8A2048F83F00A2F24C /* ACOBaseCardElement.h */,
F4960C022051FE8000780566 /* ACRView.h */,
F4960C032051FE8100780566 /* ACRView.mm */,
F4D06949205B27EA003645E4 /* ACRViewPrivate.h */,
F4960C032051FE8100780566 /* ACRView.mm */,
F4D06948205B27E9003645E4 /* ACRViewController.h */,
F4D06947205B27E9003645E4 /* ACRViewController.mm */,
F4CAE77F1F75AB9000545555 /* ACOAdaptiveCard.h */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ + (ACRCardElementType)elemType
return ACRImageSet;
}

- (UIView *)render:(UIView *)viewGroup
- (UIView *)render:(UIView<ACRIContentHoldingView> *)viewGroup
rootView:(ACRView *)rootView
inputs:(NSMutableArray *)inputs
baseCardElement:(ACOBaseCardElement *)acoElem
Expand All @@ -43,7 +43,7 @@ - (UIView *)render:(UIView *)viewGroup
rootView:rootView];
[view registerClass:[UICollectionViewCell class]forCellWithReuseIdentifier:@"cellId"];

[(UIStackView *)viewGroup addArrangedSubview:view];
[viewGroup addArrangedSubview:view];
[viewGroup addConstraint:
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "ACRBaseActionElementRenderer.h"
#import "ACRColumnSetView.h"
#import "ACRColumnView.h"
#import "ACRImageRenderer.h"
#import "ACRRegistration.h"
#import "ACRRendererPrivate.h"
#import "ACRSeparator.h"
Expand Down Expand Up @@ -66,6 +67,12 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr<AdaptiveCard> const &)adapt

ACRColumnView *verticalView = containingView;

if(![[ACRRegistration getInstance] isElementRendererOverriden:[ACRImageRenderer elemType]]){
NSNumber *number = [NSNumber numberWithUnsignedLongLong:(unsigned long long)adaptiveCard.get()];
NSString *key = [number stringValue];
NSString *urlStr = [NSString stringWithCString:adaptiveCard->GetBackgroundImage().c_str() encoding:[NSString defaultCStringEncoding]];
[rootView loadImage:urlStr key:key];
}
if(!body.empty()) {
ACRContainerStyle style = ([config getHostConfig]->adaptiveCard.allowCustomStyle)? (ACRContainerStyle)adaptiveCard->GetStyle() : ACRDefault;
style = (style == ACRNone)? ACRDefault : style;
Expand Down
Loading

0 comments on commit a61c17e

Please sign in to comment.