From 135988f2c44dd277ff53c45fb943d4c2ad22bf82 Mon Sep 17 00:00:00 2001 From: nesalang Date: Fri, 20 Aug 2021 15:07:14 -0700 Subject: [PATCH 01/14] [iOS] Fixed #6219 --- ...groundImage.FillMode.Cover.SmallImage.json | 6 +-- .../AdaptiveCards.xcodeproj/project.pbxproj | 14 ++++-- .../AdaptiveCards/ACRContainerRenderer.mm | 17 ++++--- .../AdaptiveCards/AdaptiveCards/UtiliOS.h | 4 ++ .../AdaptiveCards/AdaptiveCards/UtiliOS.mm | 32 ++++++++++-- .../AdaptiveCardsUtiliOSTest.mm | 50 +++++++++++++++++++ 6 files changed, 104 insertions(+), 19 deletions(-) create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsUtiliOSTest.mm diff --git a/samples/v1.2/Tests/AdaptiveCard.BackgroundImage.FillMode.Cover.SmallImage.json b/samples/v1.2/Tests/AdaptiveCard.BackgroundImage.FillMode.Cover.SmallImage.json index 68217b7150..66e171ee39 100644 --- a/samples/v1.2/Tests/AdaptiveCard.BackgroundImage.FillMode.Cover.SmallImage.json +++ b/samples/v1.2/Tests/AdaptiveCard.BackgroundImage.FillMode.Cover.SmallImage.json @@ -9,14 +9,14 @@ "items" : [ { "type" : "TextBlock", - "text" : "Every elements below this has to be convered by a purple background image", + "text" : "Every elements below this has to be convered by background images", "wrap" : true }, { "type" : "Container", "backgroundImage" : { - "url" : "http://104.211.228.145:3000/images/assets/icons/Background.png" + "url" : "https://picsum.photos/50" }, "items" : [ { @@ -104,7 +104,7 @@ { "type": "Container", "backgroundImage": { - "url": "http://104.211.228.145:3000/images/assets/icons/Background.png", + "url": "https://picsum.photos/50", "fillMode": "Cover" }, "items": [ diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 8b3692deef..d211a49b39 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -44,8 +44,9 @@ 6B124C9526B8AE72007E9641 /* MockContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9426B8AE72007E9641 /* MockContext.mm */; }; 6B124C9826B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */; }; 6B124C9B26B9F7AD007E9641 /* ACOVisibilityManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9A26B9F7AD007E9641 /* ACOVisibilityManager.mm */; }; - 6B124C9D26B9FA64007E9641 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B124C9C26B9FA64007E9641 /* UIKit.framework */; }; 6B124C9F26BCB2FE007E9641 /* ACOVisibilityManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B124C9926B9F6B1007E9641 /* ACOVisibilityManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6B124CA426D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124CA326D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm */; }; + 6B124CA826D050DB007E9641 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B124CA726D050DB007E9641 /* CoreGraphics.framework */; }; 6B22425D21E80647000ACDA1 /* ACOParseContextPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B22425A21E80647000ACDA1 /* ACOParseContextPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6B22425E21E80647000ACDA1 /* ACOParseContext.h in Headers */ = {isa = PBXBuildFile; fileRef = 6B22425B21E80647000ACDA1 /* ACOParseContext.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6B22425F21E80647000ACDA1 /* ACOParseContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B22425C21E80647000ACDA1 /* ACOParseContext.mm */; }; @@ -471,7 +472,9 @@ 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdaptiveCardsColumnTests.mm; sourceTree = ""; }; 6B124C9926B9F6B1007E9641 /* ACOVisibilityManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACOVisibilityManager.h; sourceTree = ""; }; 6B124C9A26B9F7AD007E9641 /* ACOVisibilityManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOVisibilityManager.mm; sourceTree = ""; }; - 6B124C9C26B9FA64007E9641 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + 6B124CA326D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdaptiveCardsUtiliOSTest.mm; sourceTree = ""; }; + 6B124CA526D050C9007E9641 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 6B124CA726D050DB007E9641 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; 6B22425A21E80647000ACDA1 /* ACOParseContextPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOParseContextPrivate.h; sourceTree = ""; }; 6B22425B21E80647000ACDA1 /* ACOParseContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOParseContext.h; sourceTree = ""; }; 6B22425C21E80647000ACDA1 /* ACOParseContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOParseContext.mm; sourceTree = ""; }; @@ -864,6 +867,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 6B124CA826D050DB007E9641 /* CoreGraphics.framework in Frameworks */, 6B124C8E26B4B3CD007E9641 /* UIKit.framework in Frameworks */, F423C0BF1EE1FBAA00905679 /* AdaptiveCards.framework in Frameworks */, ); @@ -893,7 +897,7 @@ ); name = Columns; sourceTree = ""; - }; + }; 6B124C9E26BA0D49007E9641 /* Action.ToggleVisibility */ = { isa = PBXGroup; children = ( @@ -1134,6 +1138,7 @@ F423C0C21EE1FBAA00905679 /* AdaptiveCardsTests */ = { isa = PBXGroup; children = ( + 6B124CA326D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm */, 6B124C8B26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm */, 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */, 30D56DEE2682AB9C00D6E418 /* AdaptiveCardsTextBlockTests.mm */, @@ -1435,6 +1440,8 @@ F45A07191EF4BC44007C6503 /* Frameworks */ = { isa = PBXGroup; children = ( + 6B124CA726D050DB007E9641 /* CoreGraphics.framework */, + 6B124CA526D050C9007E9641 /* Foundation.framework */, 6B124C8D26B4B3CD007E9641 /* UIKit.framework */, 6BB2121721001596009EA1BA /* AVFoundation.framework */, 6BB2120F210013AA009EA1BA /* AVKit.framework */, @@ -1971,6 +1978,7 @@ buildActionMask = 2147483647; files = ( 6B124C8C26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm in Sources */, + 6B124CA426D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm in Sources */, 30D56DEF2682AB9C00D6E418 /* AdaptiveCardsTextBlockTests.mm in Sources */, 6B124C9526B8AE72007E9641 /* MockContext.mm in Sources */, 30D56DE9268298B300D6E418 /* AdaptiveCardsTests.mm in Sources */, diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index f54a83492b..376f32dc20 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -85,14 +85,15 @@ - (UIView *)render:(UIView *)viewGroup [container setClipsToBounds:NO]; if (containerElem->GetMinHeight() > 0) { - [NSLayoutConstraint constraintWithItem:container - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:containerElem->GetMinHeight()] - .active = YES; + NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:container + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1 + constant:containerElem->GetMinHeight()]; + constraint.priority = UILayoutPriorityDefaultHigh; + constraint.active = YES; } if (leadingBlankSpace != nil && trailingBlankSpace != nil) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h index debc6e73f4..0ac1e23f59 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h @@ -20,6 +20,8 @@ using namespace AdaptiveCards; +extern const CGFloat kACRScalerTolerance; + // configures tag and initial visibility of the given view. Toggle visibility action // will access the view by the tag to change the visibility. void configVisibility(UIView *view, std::shared_ptr const &visibilityInfo); @@ -127,3 +129,5 @@ UIImage *scaleImageToSize(UIImage *image, CGSize newSize); NSNumber *iOSInternalIdHash(const std::size_t internalIdHash); id traverseResponderChainForUIViewController(UIView *view); + +CGRect FindClosestRectToCover(CGRect coverRect, CGRect targetRectToCover); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm index 53f94e1aad..91b6f620ae 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm @@ -25,6 +25,9 @@ using namespace AdaptiveCards; +// tolerance value for computing scaler for background cover size +const CGFloat kACRScalerTolerance = 0.025f; + void configVisibility(UIView *view, std::shared_ptr const &visibilityInfo) { if (!visibilityInfo->GetIsVisible()) { @@ -216,11 +219,12 @@ void renderBackgroundCoverMode(UIView *backgroundView, ACRContentStackView *targ } else if (isDeficientInHeight) { configWidthAndHeightAnchors(targetView, imageView, true); } else { - // constraint the background image to the container's width according to the spec - [imageView.widthAnchor constraintEqualToAnchor:targetView.widthAnchor].active = YES; - if (imageView.image.size.width > 0) { - [imageView.widthAnchor constraintEqualToAnchor:imageView.heightAnchor multiplier:imageView.image.size.height / imageView.image.size.width].active = YES; - } + // scale background image view to the minimum size that can still cover all of the target view. + CGRect newCoverRect = FindClosestRectToCover(CGRectMake(0, 0, sourceSize.width, sourceSize.height), targetView.frame); + NSArray *constraints = @[ [imageView.widthAnchor constraintEqualToConstant:newCoverRect.size.width], + [imageView.heightAnchor constraintEqualToConstant:newCoverRect.size.height] ]; + + [NSLayoutConstraint activateConstraints:constraints]; } } @@ -1017,3 +1021,21 @@ id traverseResponderChainForUIViewController(UIView *view) return nil; } } + +/// returns CGRect that covers target rect while maintaining the aspect ratio of coverRect +CGRect FindClosestRectToCover(CGRect coverRect, CGRect targetRectToCover) +{ + // do binary search upto the tolerance value + CGFloat scalerLowBound = 0.0f, scalerHighBound = 1.0f, scalerMidPoint = 0.0f; + while (abs(scalerLowBound - scalerHighBound) > kACRScalerTolerance) { + scalerMidPoint = (scalerLowBound + scalerHighBound) / 2.0f; + CGFloat scaledWidth = coverRect.size.width * scalerMidPoint; + CGFloat scaledHeight = coverRect.size.height * scalerMidPoint; + if (scaledWidth > targetRectToCover.size.width && scaledHeight > targetRectToCover.size.height) { + scalerHighBound = scalerMidPoint; + } else { + scalerLowBound = scalerMidPoint; + } + } + return CGRectMake(0, 0, coverRect.size.width * scalerMidPoint, coverRect.size.height * scalerMidPoint); +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsUtiliOSTest.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsUtiliOSTest.mm new file mode 100644 index 0000000000..b46d6d705b --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsUtiliOSTest.mm @@ -0,0 +1,50 @@ +// +// AdaptiveCardsUtiliOSTest.mm +// AdaptiveCardsUtiliOSTest +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import "UtiliOS.h" +#import +#import + +@interface AdaptiveCardsUtiliOSTest : XCTestCase + +@end + +using namespace AdaptiveCards; + +@implementation AdaptiveCardsUtiliOSTest + +- (void)setUp +{ + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testFindTheClosestCGRect0 +{ + CGRect rect1 = CGRectMake(0, 0, 500, 500); + CGRect rect2 = CGRectMake(0, 0, 350, 150); + CGRect rrect = FindClosestRectToCover(rect1, rect2); + XCTAssertTrue(abs(rrect.size.width - 350) / 350.0f <= kACRScalerTolerance); + XCTAssertTrue(abs(rrect.size.height - 350) / 350.0f <= kACRScalerTolerance); +} + +- (void)testFindTheClosestCGRect1 +{ + CGRect rect1 = CGRectMake(0, 0, 350, 150); + CGRect rect2 = CGRectMake(0, 0, 350, 150); + CGRect rrect = FindClosestRectToCover(rect1, rect2); + XCTAssertTrue(abs(rrect.size.width - 350) / rect2.size.width <= kACRScalerTolerance); + XCTAssertTrue(abs(rrect.size.height - 150) / rect2.size.height <= kACRScalerTolerance); +} + + +@end + From 09cf57c784b38f524bd7e3a128cfdb42cebb279d Mon Sep 17 00:00:00 2001 From: nesalang Date: Mon, 23 Aug 2021 18:24:27 -0700 Subject: [PATCH 02/14] [iOS] adding padding support to toggle visibility --- samples/v1.0/Elements/Action.OpenUrl.json | 57 +++++++++++----- .../AdaptiveCards/ACOVisibilityManager.h | 5 +- .../AdaptiveCards/ACOVisibilityManager.mm | 65 +++++++++++++++---- .../AdaptiveCards/ACRColumnView.mm | 2 +- .../AdaptiveCards/ACRFactSetRenderer.mm | 26 ++++---- .../AdaptiveCards/ACRImageRenderer.mm | 10 +-- 6 files changed, 116 insertions(+), 49 deletions(-) diff --git a/samples/v1.0/Elements/Action.OpenUrl.json b/samples/v1.0/Elements/Action.OpenUrl.json index d3f1fb8202..993ae7ff91 100644 --- a/samples/v1.0/Elements/Action.OpenUrl.json +++ b/samples/v1.0/Elements/Action.OpenUrl.json @@ -1,18 +1,43 @@ { - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "TextBlock", - "text": "This card's action will open a URL" - } - ], - "actions": [ - { - "type": "Action.OpenUrl", - "title": "Action.OpenUrl", - "url": "https://adaptivecards.io" - } - ] + "type": "AdaptiveCard", + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.3", + "body": [ + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "spacing": "Small", + "selectAction": { + "type": "Action.ToggleVisibility", + "targetElements": [ + "cardContent1", + "chevronDown1", + "chevronUp1" + ] + }, + "verticalContentAlignment": "Center", + "items": [ + { + "type": "Image", + "id": "chevronDown1", + "url": "https://adaptivecards.io/content/down.png", + "width": "20px", + "altText": "Air Travel Expense $300 collapsed" + }, + { + "type": "Image", + "id": "chevronUp1", + "url": "https://adaptivecards.io/content/up.png", + "width": "20px", + "altText": "Air Travel Expense $300 expanded", + "isVisible": false + } + ], + "width": "auto" + } + ] + } + ] } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h index b65ab3b185..64002cc712 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h @@ -17,7 +17,6 @@ @interface ACOVisibilityManager : NSObject -@property (weak) UIView *padding; @property (copy) NSString *columnWidth; /// hides viewToBeHidden from arrangedSubViews @@ -25,4 +24,8 @@ - (void)unhideView:(UIView *)viewToBeUnhidden arrangedSubviews:(NSArray *)subviews; +- (void)addPadding:(UIView *)padding; + +- (BOOL)isPadding:(UIView *)padding; + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm index d5ce30e36c..a4d6683d86 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm @@ -10,7 +10,17 @@ #import "ACREnums.h" #import "ACRSeparator.h" -@implementation ACOVisibilityManager +@implementation ACOVisibilityManager { + NSHashTable *_paddingSet; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _paddingSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:2]; + } + return self; +} - (void)hideView:(UIView *)viewToBeHidden arrangedSubviews:(NSArray *)subviews { @@ -50,16 +60,14 @@ - (void)hideView:(UIView *)viewToBeHidden arrangedSubviews:(NSArray *) // only cares for tailing padding at the moment // if column width is "auto", and all of its children hidden, // hide column, if is "stretch", then don't hide it. - if (self.padding && [_columnWidth isEqualToString:@"auto"]) { + if (_paddingSet.count && [_columnWidth isEqualToString:@"auto"]) { for (UIView *subview in subviews) { - if (!subview.isHidden && ![subview isEqual:self.padding]) { + if (!subview.isHidden && ![self isPadding:subview]) { return; } } - if (!self.padding.isHidden) { - self.padding.hidden = YES; - } + [self changeVisibilitOfPaddingsTo:YES]; } } @@ -73,19 +81,27 @@ - (void)unhideView:(UIView *)viewToBeUnhidden arrangedSubviews:(NSArray 1 && separator && separator.isHidden) { separator.hidden = NO; } - if (isLastView && self.padding && self.padding.isHidden) { - self.padding.hidden = NO; + if (isLastView && _paddingSet.count) { + [self changeVisibilitOfPaddingsTo:NO]; + } + } +} + +- (void)addPadding:(UIView *)padding { + if (padding && _paddingSet) { + [_paddingSet addObject:padding]; + } +} + +- (BOOL)isPadding:(UIView *)padding { + if (padding && _paddingSet) { + return [_paddingSet containsObject:padding]; + } + return NO; +} + +- (void)changeVisibilitOfPaddingsTo:(BOOL)visiblity +{ + for (UIView *padding in _paddingSet) { + if (padding.isHidden != visiblity) { + padding.hidden = visiblity; } } } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index 7588a6192a..18703cdc05 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -126,7 +126,7 @@ - (void)unhideView:(UIView *)view - (UIView *)addPaddingSpace { UIView *padding = [super addPaddingSpace]; - _visibilityManager.padding = padding; + [_visibilityManager addPadding:padding]; return padding; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm index 60f54617b8..90c12f3d54 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm @@ -196,19 +196,19 @@ - (UIView *)render:(UIView *)viewGroup [viewGroup addArrangedSubview:factSetWrapperView]; - if (factSet->GetHeight() == HeightType::Stretch) { - UIView *blankTrailingSpace0 = [[UIView alloc] init]; - blankTrailingSpace0.translatesAutoresizingMaskIntoConstraints = NO; - [titleStack addArrangedSubview:blankTrailingSpace0]; - [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; - [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; - - UIView *blankTrailingSpace1 = [[UIView alloc] init]; - blankTrailingSpace1.translatesAutoresizingMaskIntoConstraints = NO; - [valueStack addArrangedSubview:blankTrailingSpace1]; - [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; - [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; - } +// if (factSet->GetHeight() == HeightType::Stretch) { +// UIView *blankTrailingSpace0 = [[UIView alloc] init]; +// blankTrailingSpace0.translatesAutoresizingMaskIntoConstraints = NO; +// [titleStack addArrangedSubview:blankTrailingSpace0]; +// [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; +// [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; +// +// UIView *blankTrailingSpace1 = [[UIView alloc] init]; +// blankTrailingSpace1.translatesAutoresizingMaskIntoConstraints = NO; +// [valueStack addArrangedSubview:blankTrailingSpace1]; +// [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; +// [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; +// } configVisibility(factSetWrapperView, elem); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index 1ae0df469e..38c004d652 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -116,11 +116,11 @@ - (UIView *)render:(UIView *)viewGroup [view setContentCompressionResistancePriority:imagePriority forAxis:UILayoutConstraintAxisVertical]; } - if (imgElem->GetHeight() == HeightType::Stretch && imgElem->GetPixelHeight() == 0) { - if ([viewGroup isKindOfClass:[ACRColumnView class]]) { - [(ACRColumnView *)viewGroup addPaddingSpace]; - } - } +// if (imgElem->GetHeight() == HeightType::Stretch && imgElem->GetPixelHeight() == 0) { +// if ([viewGroup isKindOfClass:[ACRColumnView class]]) { +// [(ACRColumnView *)viewGroup addPaddingSpace]; +// } +// } std::shared_ptr selectAction = imgElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; From 9bbb020080870e7b95afa57a565d32b8bea6e5ba Mon Sep 17 00:00:00 2001 From: nesalang Date: Thu, 26 Aug 2021 16:30:06 -0700 Subject: [PATCH 03/14] [Padding] Update --- samples/v1.0/Elements/Action.OpenUrl.json | 176 +++++++++++++++--- ...mnSet.Input.ChoiceSet.VerticalStretch.json | 64 +------ .../xcschemes/ADCIOSVisualizer.xcscheme | 2 +- .../AdaptiveCards.xcodeproj/project.pbxproj | 26 ++- .../AdaptiveCards-Universal.xcscheme | 2 +- .../xcschemes/AdaptiveCards.xcscheme | 11 +- .../AdaptiveCards/ACOPaddingHandler.h | 25 +++ .../AdaptiveCards/ACOPaddingHandler.mm | 89 +++++++++ .../AdaptiveCards/ACRColumnRenderer.mm | 23 +-- .../AdaptiveCards/ACRColumnView.h | 8 +- .../AdaptiveCards/ACRColumnView.mm | 22 +++ .../AdaptiveCards/ACRContainerRenderer.mm | 19 +- .../AdaptiveCards/ACRFactSetRenderer.mm | 14 -- .../AdaptiveCards/ACRImageRenderer.mm | 8 +- .../ACRInputChoiceSetRenderer.mm | 16 +- .../AdaptiveCards/ACRInputDateRenderer.mm | 16 +- .../AdaptiveCards/ACRInputNumberRenderer.mm | 15 +- .../AdaptiveCards/ACRInputRenderer.mm | 16 +- .../AdaptiveCards/ACRInputTimeRenderer.mm | 14 +- .../AdaptiveCards/ACRInputToggleRenderer.mm | 15 +- .../AdaptiveCards/ACRRenderer.mm | 51 ++--- .../AdaptiveCardsColumnTests.mm | 66 ++++++- 22 files changed, 413 insertions(+), 285 deletions(-) create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm diff --git a/samples/v1.0/Elements/Action.OpenUrl.json b/samples/v1.0/Elements/Action.OpenUrl.json index 993ae7ff91..5b47c851a7 100644 --- a/samples/v1.0/Elements/Action.OpenUrl.json +++ b/samples/v1.0/Elements/Action.OpenUrl.json @@ -1,43 +1,163 @@ { "type": "AdaptiveCard", - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "version": "1.3", "body": [ { - "type": "ColumnSet", "columns": [ { - "type": "Column", - "spacing": "Small", - "selectAction": { - "type": "Action.ToggleVisibility", - "targetElements": [ - "cardContent1", - "chevronDown1", - "chevronUp1" - ] - }, - "verticalContentAlignment": "Center", + "width": "40", "items": [ { - "type": "Image", - "id": "chevronDown1", - "url": "https://adaptivecards.io/content/down.png", - "width": "20px", - "altText": "Air Travel Expense $300 collapsed" + "columns": [ + { + "width": "auto", + "items": [ + { + "color": "accent", + "text": "Requested", + "id": "approvalStatus", + "spacing": "padding", + "type": "TextBlock" + } + ], + "backgroundImage": { + "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAAhSURBVDiNY3z69e1/BjIAEzmaRjWOahzVOKpxVONw0AgAMwoD/kUfY7QAAAAASUVORK5CYII=", + "fillMode": "repeatHorizontally", + "verticalAlignment": "center" + }, + "verticalContentAlignment": "center", + "minHeight": "28px", + "spacing": "padding", + "bleed": false, + "type": "Column" + }, + { + "width": "auto", + "items": [ + + ], + "type": "Column" + } + ], + "type": "ColumnSet" + } + ], + "verticalContentAlignment": "center", + "type": "Column" + } + ], + "id": "infoTag", + "type": "ColumnSet" + }, + { + "maxLines": 2, + "size": "large", + "text": "Test", + "weight": "bolder", + "wrap": true, + "id": "approvalTitle", + "spacing": "none", + "type": "TextBlock" + }, + { + "items": [ + { + "columns": [ + { + "width": "120px", + "items": [ + { + "maxLines": 5, + "text": "Requested by", + "weight": "bolder", + "wrap": true, + "type": "TextBlock" + } + ], + "type": "Column" + }, + { + "width": "stretch", + "items": [ + { + "maxLines": 5, + "text": "Isaiah Langer", + "wrap": true, + "type": "TextBlock" + } + ], + "type": "Column" + } + ], + "spacing": "none", + "type": "ColumnSet" + }, + { + "columns": [ + { + "width": "120px", + "items": [ + { + "maxLines": 5, + "text": "Pending response", + "weight": "bolder", + "wrap": true, + "type": "TextBlock" + } + ], + "type": "Column" }, { - "type": "Image", - "id": "chevronUp1", - "url": "https://adaptivecards.io/content/up.png", - "width": "20px", - "altText": "Air Travel Expense $300 expanded", - "isVisible": false + "width": "stretch", + "items": [ + { + "maxLines": 5, + "text": "Isaiah Langer", + "wrap": true, + "type": "TextBlock" + } + ], + "type": "Column" } ], - "width": "auto" + "spacing": "none", + "type": "ColumnSet" } - ] + ], + "id": "aggregatedActions", + "type": "Container" + }, + { + "actions": [ + { + "data": { + "msteams": { + "type": "task/fetch", + "title": "View Details Click" + }, + "approvalId": "611de48c-63c0-409a-8736-366d0b777b1a", + "approvalType": "Basic" + }, + "title": "View details", + "type": "Action.Submit" + } + ], + "spacing": "Large", + "type": "ActionSet" + } + ], + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "version": "1.2", + "refresh": { + "userIds": [ + "8:orgid:ddb1414c-0459-4dcf-9d2c-b8fd5f0f84c9" + ], + "action": { + "verb": "AutoRefresh", + "data": { + "approvalId": "611de48c-63c0-409a-8736-366d0b777b1a" + }, + "title": "AutoRefresh", + "type": "Action.Execute" } - ] + } } diff --git a/samples/v1.1/Tests/ColumnSet.Input.ChoiceSet.VerticalStretch.json b/samples/v1.1/Tests/ColumnSet.Input.ChoiceSet.VerticalStretch.json index 2521baeaba..6fb6b1be9e 100644 --- a/samples/v1.1/Tests/ColumnSet.Input.ChoiceSet.VerticalStretch.json +++ b/samples/v1.1/Tests/ColumnSet.Input.ChoiceSet.VerticalStretch.json @@ -13,7 +13,7 @@ "items": [ { "type": "TextBlock", - "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", "wrap": true } ] @@ -76,68 +76,6 @@ "wrap": true } ] - }, - { - "type": "Column", - "width": 1, - "spacing": "large", - "separator": true, - "items": [ - { - "type": "TextBlock", - "text": "This is a textblock that doesn't stretch, but the input text does", - "wrap": true - }, - { - "type": "Input.ChoiceSet", - "id": "myColor3", - "isMultiSelect": true, - "value": "1,3", - "style": "compact", - "height": "stretch", - "choices": [ - { - "title": "Red", - "value": "1" - }, - { - "title": "Green", - "value": "2" - }, - { - "title": "Blue", - "value": "3" - } - ] - }, - { - "type": "Input.ChoiceSet", - "id": "myColor4", - "isMultiSelect": true, - "value": "1", - "style": "expanded", - "height": "stretch", - "choices": [ - { - "title": "Red", - "value": "1" - }, - { - "title": "Green", - "value": "2" - }, - { - "title": "Blue", - "value": "3" - } - ] - }, - { - "type": "TextBlock", - "text": "One last text block at the bottom", - "wrap": true - } - ] } ] } diff --git a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme index 1693d7cac1..426958bc9f 100644 --- a/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme +++ b/source/ios/AdaptiveCards/ADCIOSVisualizer/ADCIOSVisualizer.xcodeproj/xcshareddata/xcschemes/ADCIOSVisualizer.xcscheme @@ -1,6 +1,6 @@ - - - - diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h new file mode 100644 index 0000000000..273f78c401 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h @@ -0,0 +1,25 @@ +// +// ACOPaddingHandler.h +// AdaptiveCards +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import +#import + +@class ACOBaseCardElement; + +@interface ACOPaddingHandler : NSObject + +@property (nonatomic) BOOL hasPadding; + +- (UIView *)configurePaddingFor:(UIView *)view; + +- (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; + +- (NSArray *)activateConstraintsForPadding; + +- (BOOL)isPadding:(UIView *)padding; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm new file mode 100644 index 0000000000..4976ef4049 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm @@ -0,0 +1,89 @@ +// +// ACOPaddingHandler.mm +// AdaptiveCards +// +// Copyright © 2021 Microsoft. All rights reserved. +// +// + +#import "ACOPaddingHandler.h" +#import "ACOBaseCardElementPrivate.h" + +@implementation ACOPaddingHandler { + NSMapTable *> *_paddingMap; + NSHashTable *_paddingSet; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _paddingMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory]; + _paddingSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; + } + return self; +} + +- (BOOL)hasPadding +{ + if (_paddingSet) { + return _paddingSet.count != 0; + } + return NO; +} + +- (UIView *)configurePaddingFor:(UIView *)view +{ + UIView *blankTrailingSpace = [[UIView alloc] init]; + blankTrailingSpace.translatesAutoresizingMaskIntoConstraints = NO; + [blankTrailingSpace setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + NSMutableArray *values = [_paddingMap objectForKey:view]; + if (!values) { + values = [[NSMutableArray alloc] init]; + [_paddingMap setObject:values forKey:view]; + } + + [values addObject:[NSValue valueWithNonretainedObject:blankTrailingSpace]]; + [_paddingSet addObject:blankTrailingSpace]; + return blankTrailingSpace; +} + +- (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement +{ + UIView *blankTrailingSpace = nil; + + if (HeightType::Stretch == correspondingElement.element->GetHeight()) { + blankTrailingSpace = [self configurePaddingFor:view]; + } + + return blankTrailingSpace; +} + +- (NSArray *)activateConstraintsForPadding +{ + if (_paddingSet.count > 1) { + NSMutableArray *constraints = [[NSMutableArray alloc] init]; + UIView *prevPadding = nil; + for (NSArray *values in _paddingMap.objectEnumerator) { + for (NSValue *value in values) { + UIView *padding = [value nonretainedObjectValue]; + if (prevPadding && padding) { + [constraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; + constraints.lastObject.priority = UILayoutPriorityDefaultLow; + } + + prevPadding = padding; + } + } + [NSLayoutConstraint activateConstraints:constraints]; + return constraints; + } + return nil; +} + +- (BOOL)isPadding:(UIView *)padding +{ + return [_paddingSet containsObject:padding]; +} + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm index 3d3e749cb6..4bc73b9596 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm @@ -66,7 +66,8 @@ - (UIView *)render:(UIView *)viewGroup UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { - leadingBlankSpace = [column addPaddingSpace]; + leadingBlankSpace = [column configurePaddingFor:column]; + [column addArrangedSubview:leadingBlankSpace]; } ACRColumnSetView *columnsetView = (ACRColumnSetView *)viewGroup; @@ -80,15 +81,12 @@ - (UIView *)render:(UIView *)viewGroup andHostConfig:acoConfig]; if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Top && _fillAlignment)) { - trailingBlankSpace = [column addPaddingSpace]; - } - - if (leadingBlankSpace || trailingBlankSpace) { - column.hasStretchableView = YES; + trailingBlankSpace = [column configurePaddingFor:column]; + [column addArrangedSubview:trailingBlankSpace]; } if (!column.hasStretchableView) { - [column addPaddingSpace]; + [column addArrangedSubview:[column configurePaddingFor:column]]; column.hasPaddingView = YES; } @@ -112,16 +110,7 @@ - (UIView *)render:(UIView *)viewGroup column.shouldGroupAccessibilityChildren = YES; - if (leadingBlankSpace != nil && trailingBlankSpace != nil) { - [NSLayoutConstraint constraintWithItem:leadingBlankSpace - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:trailingBlankSpace - attribute:NSLayoutAttributeHeight - multiplier:1.0 - constant:0] - .active = YES; - } + [column activatePaddingConstraints]; // config visibility for column view followed by configuring visibility of the items of column configVisibility(column, elem); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index f936c7c604..088d8f043f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -21,10 +21,16 @@ typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { @property CGFloat pixelWidth; @property CGFloat relativeWidth; @property BOOL hasMoreThanOneRelativeWidth; -@property BOOL hasStretchableView; +@property (nonatomic) BOOL hasStretchableView; @property BOOL hasPaddingView; @property BOOL isLastColumn; @property NSMutableArray *inputHandlers; @property (weak) ACRColumnSetView *columnsetView; +- (UIView *)configurePaddingFor:(UIView *)view; + +- (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element; + +- (void)activatePaddingConstraints; + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index 18703cdc05..f4dd4ad08a 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -7,9 +7,11 @@ #import "ACRColumnView.h" #import "ACOVisibilityManager.h" +#import "ACOPaddingHandler.h" @implementation ACRColumnView { ACOVisibilityManager *_visibilityManager; + ACOPaddingHandler *_paddingHandler; } - (void)setColumnWidth:(NSString *)columnWidth @@ -25,6 +27,7 @@ - (void)config:(nullable NSDictionary *)attributes self.isLastColumn = NO; self.inputHandlers = [[NSMutableArray alloc] init]; _visibilityManager = [[ACOVisibilityManager alloc] init]; + _paddingHandler = [[ACOPaddingHandler alloc] init]; } - (void)addArrangedSubview:(UIView *)view @@ -123,6 +126,10 @@ - (void)unhideView:(UIView *)view [_visibilityManager unhideView:view arrangedSubviews:[self getContentStackSubviews]]; } +- (BOOL)hasStretchableView { + return _paddingHandler.hasPadding; +} + - (UIView *)addPaddingSpace { UIView *padding = [super addPaddingSpace]; @@ -130,5 +137,20 @@ - (UIView *)addPaddingSpace return padding; } +- (UIView *)configurePaddingFor:(UIView *)view +{ + return [_paddingHandler configurePaddingFor:view]; +} + +- (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element +{ + return [_paddingHandler configurePaddingFor:view correspondingElement:element]; +} + +- (void)activatePaddingConstraints +{ + [_paddingHandler activateConstraintsForPadding]; +} + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index 376f32dc20..379e2bd888 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -65,7 +65,8 @@ - (UIView *)render:(UIView *)viewGroup UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; if (containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { - leadingBlankSpace = [container addPaddingSpace]; + leadingBlankSpace = [container configurePaddingFor:container]; + [container addArrangedSubview:leadingBlankSpace]; } container.frame = viewGroup.frame; @@ -79,7 +80,8 @@ - (UIView *)render:(UIView *)viewGroup const VerticalContentAlignment adaptiveVAlignment = containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top); // Dont add the trailing space if the vertical content alignment is top/default if (adaptiveVAlignment == VerticalContentAlignment::Center || (adaptiveVAlignment == VerticalContentAlignment::Top && !(container.hasStretchableView))) { - trailingBlankSpace = [container addPaddingSpace]; + trailingBlankSpace = [container configurePaddingFor:container]; + [container addArrangedSubview:trailingBlankSpace]; } [container setClipsToBounds:NO]; @@ -96,17 +98,6 @@ - (UIView *)render:(UIView *)viewGroup constraint.active = YES; } - if (leadingBlankSpace != nil && trailingBlankSpace != nil) { - [NSLayoutConstraint constraintWithItem:leadingBlankSpace - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:trailingBlankSpace - attribute:NSLayoutAttributeHeight - multiplier:1.0 - constant:0] - .active = YES; - } - std::shared_ptr selectAction = containerElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; [container configureForSelectAction:acoSelectAction rootView:rootView]; @@ -114,6 +105,8 @@ - (UIView *)render:(UIView *)viewGroup [container hideIfSubviewsAreAllHidden]; + [container activatePaddingConstraints]; + [rootView.context popBaseCardElementContext:acoElem]; return viewGroup; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm index 90c12f3d54..1f0faa30ab 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm @@ -196,20 +196,6 @@ - (UIView *)render:(UIView *)viewGroup [viewGroup addArrangedSubview:factSetWrapperView]; -// if (factSet->GetHeight() == HeightType::Stretch) { -// UIView *blankTrailingSpace0 = [[UIView alloc] init]; -// blankTrailingSpace0.translatesAutoresizingMaskIntoConstraints = NO; -// [titleStack addArrangedSubview:blankTrailingSpace0]; -// [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; -// [blankTrailingSpace0 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; -// -// UIView *blankTrailingSpace1 = [[UIView alloc] init]; -// blankTrailingSpace1.translatesAutoresizingMaskIntoConstraints = NO; -// [valueStack addArrangedSubview:blankTrailingSpace1]; -// [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal]; -// [blankTrailingSpace1 setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisVertical]; -// } - configVisibility(factSetWrapperView, elem); return factSetWrapperView; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index 38c004d652..9fe4791960 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -115,13 +115,7 @@ - (UIView *)render:(UIView *)viewGroup [view setContentCompressionResistancePriority:imagePriority forAxis:UILayoutConstraintAxisHorizontal]; [view setContentCompressionResistancePriority:imagePriority forAxis:UILayoutConstraintAxisVertical]; } - -// if (imgElem->GetHeight() == HeightType::Stretch && imgElem->GetPixelHeight() == 0) { -// if ([viewGroup isKindOfClass:[ACRColumnView class]]) { -// [(ACRColumnView *)viewGroup addPaddingSpace]; -// } -// } - + std::shared_ptr selectAction = imgElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; // instantiate and add tap gesture recognizer diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm index 81486dd760..a7d2c988d8 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputChoiceSetRenderer.mm @@ -66,20 +66,8 @@ - (UIView *)render:(UIView *)viewGroup [inputs addObject:inputLabelView]; - if (elem->GetHeight() == HeightType::Stretch) { - ACRColumnView *textInputContainer = [[ACRColumnView alloc] init]; - [textInputContainer addArrangedSubview:inputLabelView]; - - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [textInputContainer addArrangedSubview:blankTrailingSpace]; - [textInputContainer adjustHuggingForLastElement]; - - [viewGroup addArrangedSubview:textInputContainer]; - } else { - [viewGroup addArrangedSubview:inputLabelView]; - } - + [viewGroup addArrangedSubview:inputLabelView]; + configVisibility(inputLabelView, elem); return inputLabelView; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputDateRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputDateRenderer.mm index f464855275..e784348ee0 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputDateRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputDateRenderer.mm @@ -39,20 +39,8 @@ - (UIView *)render:(UIView *)viewGroup ACRInputLabelView *inputLabelView = [[ACRInputLabelView alloc] initInputLabelView:rootView acoConfig:acoConfig adptiveInputElement:dateInput inputView:dateField accessibilityItem:dateField.inputView viewGroup:viewGroup dataSource:nil]; dateField.accessibilityTraits = UIAccessibilityTraitButton | UIAccessibilityTraitStaticText; - - if (elem->GetHeight() == HeightType::Stretch) { - ACRColumnView *inputContainer = [[ACRColumnView alloc] init]; - [inputContainer addArrangedSubview:inputLabelView]; - - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [inputContainer addArrangedSubview:blankTrailingSpace]; - [inputContainer adjustHuggingForLastElement]; - - [viewGroup addArrangedSubview:inputContainer]; - } else { - [viewGroup addArrangedSubview:inputLabelView]; - } + + [viewGroup addArrangedSubview:inputLabelView]; [inputs addObject:inputLabelView]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm index c5e2a92f55..27716c4e89 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputNumberRenderer.mm @@ -48,19 +48,8 @@ - (UIView *)render:(UIView *)viewGroup numInput.text = numberInputHandler.text; ACRInputLabelView *inputLabelView = [[ACRInputLabelView alloc] initInputLabelView:rootView acoConfig:acoConfig adptiveInputElement:numInputBlck inputView:numInput accessibilityItem:numInput viewGroup:viewGroup dataSource:numberInputHandler]; - - if (elem->GetHeight() == HeightType::Stretch) { - ACRColumnView *inputContainer = [[ACRColumnView alloc] init]; - [inputContainer addArrangedSubview:inputLabelView]; - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [inputContainer addArrangedSubview:blankTrailingSpace]; - [inputContainer adjustHuggingForLastElement]; - - [viewGroup addArrangedSubview:inputContainer]; - } else { - [viewGroup addArrangedSubview:inputLabelView]; - } + + [viewGroup addArrangedSubview:inputLabelView]; [inputs addObject:inputLabelView]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm index ffef0b9eeb..944907cc03 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm @@ -164,20 +164,8 @@ - (UIView *)render:(UIView *)viewGroup [inputview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; } - - if (elem->GetHeight() == HeightType::Stretch && !inputBlck->GetIsMultiline()) { - ACRColumnView *textInputContainer = [[ACRColumnView alloc] init]; - [textInputContainer addArrangedSubview:inputview]; - - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [textInputContainer addArrangedSubview:blankTrailingSpace]; - [textInputContainer adjustHuggingForLastElement]; - - [viewGroup addArrangedSubview:textInputContainer]; - } else { - [viewGroup addArrangedSubview:inputview]; - } + + [viewGroup addArrangedSubview:inputview]; inputview.translatesAutoresizingMaskIntoConstraints = false; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTimeRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTimeRenderer.mm index 21055d5929..8c471b2e4e 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTimeRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputTimeRenderer.mm @@ -41,19 +41,7 @@ - (UIView *)render:(UIView *)viewGroup UIView *renderedview = inputLabelView; if (viewGroup) { - if (elem->GetHeight() == HeightType::Stretch) { - ACRColumnView *inputContainer = [[ACRColumnView alloc] init]; - [inputContainer addArrangedSubview:renderedview]; - - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [inputContainer addArrangedSubview:blankTrailingSpace]; - [inputContainer adjustHuggingForLastElement]; - [viewGroup addArrangedSubview:inputContainer]; - renderedview = inputContainer; - } else { - [viewGroup addArrangedSubview:renderedview]; - } + [viewGroup addArrangedSubview:renderedview]; } [inputs addObject:renderedview]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm index 8d8ee942a1..eb6cfb9ed5 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputToggleRenderer.mm @@ -68,19 +68,8 @@ - (UIView *)render:(UIView *)viewGroup } [inputs addObject:inputLabelView]; - - if (elem->GetHeight() == HeightType::Stretch) { - ACRColumnView *textInputContainer = [[ACRColumnView alloc] init]; - [textInputContainer addArrangedSubview:inputLabelView]; - // Add a blank view so the input field doesnt grow as large as it can and so it keeps the same behavior as Android and UWP - UIView *blankTrailingSpace = [[UIView alloc] init]; - [textInputContainer addArrangedSubview:blankTrailingSpace]; - [textInputContainer adjustHuggingForLastElement]; - - [viewGroup addArrangedSubview:textInputContainer]; - } else { - [viewGroup addArrangedSubview:inputLabelView]; - } + + [viewGroup addArrangedSubview:inputLabelView]; configVisibility(inputLabelView, elem); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm index ff462334de..fab3131eeb 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm @@ -178,14 +178,18 @@ + (UIView *)render:(UIView *)view ACRRegistration *reg = [ACRRegistration getInstance]; ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] init]; ACOFeatureRegistration *featureReg = [ACOFeatureRegistration getInstance]; - - UIView *prevStretchableElem = nil, *curStretchableElem = nil; + ACRColumnView *columnView = nil; + + if ([view isKindOfClass:[ACRColumnView class]]) { + columnView = (ACRColumnView *)view; + } + UIView *renderedView = nil; auto firstelem = elems.begin(); for (const auto &elem : elems) { ACRSeparator *separator = nil; - if (*firstelem != elem && curStretchableElem) { + if (*firstelem != elem && renderedView) { separator = [ACRSeparator renderSeparation:elem forSuperview:view withHostConfig:[config getHostConfig]]; @@ -207,40 +211,17 @@ + (UIView *)render:(UIView *)view @throw [ACOFallbackException fallbackException]; } - curStretchableElem = [renderer render:view rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:config]; - - if (separator && !curStretchableElem) { - [(ACRContentStackView *)view removeViewFromContentStackView:separator]; - } - - if (elem->GetHeight() == HeightType::Stretch && curStretchableElem) { - // vertical stretch works in the following way: - // an ui element that will be stretched will be contained in a new superview. - // additional trailing view is added to the superview at the bottom - // uistackview for ColumnSet will have distribution set to fill - // this ensures all columns will have same height, thus making the space available for - // stretch. - // when a smaller column is expanded, the trailing views in its subviews will fill up the - // space, by setting the heights of the superviews of the trailing views same as below, - // filler space occupies the same space. - if (prevStretchableElem) { - NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:curStretchableElem - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:prevStretchableElem - attribute:NSLayoutAttributeHeight - multiplier:1 - constant:0]; - heightConstraint.priority = UILayoutPriorityDefaultLow; - heightConstraint.active = YES; - } - - if ([view isKindOfClass:[ACRColumnView class]]) { - ACRColumnView *columnView = (ACRColumnView *)view; - columnView.hasStretchableView = YES; + renderedView = [renderer render:view rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:config]; + + if (columnView) { + UIView *padding = [columnView configPadding:renderedView acoElement:acoElem]; + if (padding) { + [view addArrangedSubview:padding]; } + } - prevStretchableElem = curStretchableElem; + if (separator && !renderedView) { + [(ACRContentStackView *)view removeViewFromContentStackView:separator]; } } @catch (ACOFallbackException *e) { handleFallbackException(e, view, rootView, inputs, elem, config); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm index 3ec268648e..cfc7237c98 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm @@ -7,9 +7,12 @@ #import "ACORenderContext.h" #import "ACOVisibilityManager.h" +#import "ACOPaddingHandler.h" #import "ACRColumnView.h" #import "ACRSeparator.h" #import "Column.h" +#import "TextBlock.h" +#import "ACOBaseCardElementPrivate.h" #import "Enums.h" #import #import @@ -112,7 +115,7 @@ - (void)testVisibilityManagerHidePadding // if column width is 'auto' and there isn't a view, padding disapears ACOVisibilityManager *manager = [[ACOVisibilityManager alloc] init]; manager.columnWidth = @"auto"; - manager.padding = padding; + [manager addPadding:padding]; [manager hideView:viewToBeHidden arrangedSubviews:arrangedSubviews]; XCTAssert(arrangedSubviews[0].hidden == YES); XCTAssert(arrangedSubviews[1].hidden == YES); @@ -130,7 +133,7 @@ - (void)testVisibilityManagerPaddingWithStretch [arrangedSubviews addObject:padding]; ACOVisibilityManager *manager = [[ACOVisibilityManager alloc] init]; manager.columnWidth = @"stretch"; - manager.padding = padding; + [manager addPadding:padding]; [manager hideView:viewToBeHidden arrangedSubviews:arrangedSubviews]; XCTAssert(arrangedSubviews[0].hidden == YES); XCTAssert(arrangedSubviews[1].hidden == NO); @@ -153,7 +156,7 @@ - (void)testVisibilityManagerDoesNotHidePadding // even if column width is 'auto', if there is a view padding doesn't disapear ACOVisibilityManager *manager = [[ACOVisibilityManager alloc] init]; manager.columnWidth = @"auto"; - manager.padding = padding; + [manager addPadding:padding]; [manager hideView:viewToBeHidden0 arrangedSubviews:arrangedSubviews]; XCTAssert(arrangedSubviews[0].hidden == YES); XCTAssert(arrangedSubviews[1].hidden == YES); @@ -180,7 +183,7 @@ - (void)testVisibilityManagerHidePaddingComplex0 // even if column width is 'auto', if there is a view padding doesn't disapear ACOVisibilityManager *manager = [[ACOVisibilityManager alloc] init]; manager.columnWidth = @"auto"; - manager.padding = padding; + [manager addPadding:padding]; [manager hideView:viewToBeHidden0 arrangedSubviews:arrangedSubviews]; [manager hideView:viewToBeHidden1 arrangedSubviews:arrangedSubviews]; XCTAssert(arrangedSubviews[0].hidden == YES); @@ -235,4 +238,59 @@ - (void)testAddColumnToContext2 XCTAssertEqual(columnView, [context retrieveVisiblityManagerWithTag:view1.tag]); } +- (void)testPaddingInitialization +{ + ACOPaddingHandler *paddingHandler = [[ACOPaddingHandler alloc] init]; + XCTAssertNotNil(paddingHandler); +} + +- (void)testPaddingConfigurePadding +{ + ACOPaddingHandler *paddingHandler = [[ACOPaddingHandler alloc] init]; + auto elem = std::make_shared(); + ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] initWithBaseCardElement:elem]; + UIView *view = [[UIView alloc] init]; + UIView *padding0 = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + XCTAssertNil(padding0); + + elem->SetHeight(HeightType::Stretch); + UIView *padding1 = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + + XCTAssertNotNil(padding1); +} + +NSArray *buildTextBlocksWithHeightStretch(uint n) { + NSMutableArray *textBlocks = [[NSMutableArray alloc] init]; + for (uint i = 0; i < n; i++) { + auto elem0 = std::make_shared(); + elem0->SetHeight(HeightType::Stretch); + ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] initWithBaseCardElement:elem0]; + [textBlocks addObject:acoElem]; + } + + return textBlocks; +} + +- (void)testPaddingActivateConstraints +{ + NSArray *textBlocks = buildTextBlocksWithHeightStretch(3); + ACOPaddingHandler *paddingHandler = [[ACOPaddingHandler alloc] init]; + UIView *superView = [[UIView alloc] init]; + for (ACOBaseCardElement *acoElem in textBlocks) { + UIView *view = [[UIView alloc] init]; + [superView addSubview:view]; + UIView *padding = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + if (padding) { + [superView addSubview:padding]; + } + XCTAssertTrue([paddingHandler isPadding:padding]); + XCTAssertFalse([paddingHandler isPadding:view]); + } + + NSArray *constraints = [paddingHandler activateConstraintsForPadding]; + XCTAssertNotNil(constraints); + XCTAssertTrue(constraints.count == 2); +} + + @end From db383969819fde033c663dcd80429aa346d555cf Mon Sep 17 00:00:00 2001 From: nesalang Date: Mon, 30 Aug 2021 11:04:33 -0700 Subject: [PATCH 04/14] [iOS] work in progress --- ...olumnSet.Input.Toggle.VerticalStretch.json | 92 +++++++++---------- .../AdaptiveCards/AdaptiveCards/ACOEnums.h | 5 + .../AdaptiveCards/ACOPaddingHandler.h | 2 +- .../AdaptiveCards/ACOPaddingHandler.mm | 11 ++- .../AdaptiveCards/ACRColumnRenderer.mm | 12 +-- .../AdaptiveCards/ACRColumnView.h | 2 +- .../AdaptiveCards/ACRColumnView.mm | 4 +- .../AdaptiveCards/ACRContainerRenderer.mm | 8 +- .../AdaptiveCards/ACRContentHoldingUIView.mm | 4 +- .../AdaptiveCards/ACRImageProperties.h | 1 + .../AdaptiveCards/ACRImageProperties.mm | 3 + .../AdaptiveCards/ACRImageRenderer.mm | 8 +- .../AdaptiveCards/AdaptiveCards/UtiliOS.h | 2 + .../AdaptiveCards/AdaptiveCards/UtiliOS.mm | 14 +++ 14 files changed, 100 insertions(+), 68 deletions(-) diff --git a/samples/v1.1/Tests/ColumnSet.Input.Toggle.VerticalStretch.json b/samples/v1.1/Tests/ColumnSet.Input.Toggle.VerticalStretch.json index bdb51e1f8f..3d4f61217c 100644 --- a/samples/v1.1/Tests/ColumnSet.Input.Toggle.VerticalStretch.json +++ b/samples/v1.1/Tests/ColumnSet.Input.Toggle.VerticalStretch.json @@ -1,48 +1,48 @@ { - "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", - "type": "AdaptiveCard", - "version": "1.0", - "body": [ - { - "type": "ColumnSet", - "height": "stretch", - "columns": [ - { - "type": "Column", - "width": 1, - "items": [ - { - "type": "TextBlock", - "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", - "wrap": true - } - ] - }, - { - "type": "Column", - "width": 1, - "spacing": "large", - "separator": true, - "items": [ - { - "type": "TextBlock", - "text": "This is a textblock that doesn't stretch, but the input text does", - "wrap": true - }, - { - "type": "Input.Toggle", - "id": "input1", - "title": "Placeholder title", - "height": "stretch" - }, - { - "type": "TextBlock", - "text": "One last text block at the bottom", - "wrap": true - } - ] - } - ] - } - ] + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "ColumnSet", + "height": "stretch", + "columns": [ + { + "type": "Column", + "width": 1, + "items": [ + { + "type": "TextBlock", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": 1, + "spacing": "large", + "separator": true, + "items": [ + { + "type": "TextBlock", + "text": "This is a textblock that doesn't stretch, but the input text does", + "wrap": true + }, + { + "type": "Input.Toggle", + "id": "input1", + "title": "Placeholder title", + "height": "stretch" + }, + { + "type": "TextBlock", + "text": "One last text block at the bottom", + "wrap": true + } + ] + } + ] + } + ] } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h index 4d6dddaec0..0a19a352a1 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h @@ -86,3 +86,8 @@ typedef NS_ENUM(NSUInteger, ACRVerticalAlignment) { ACRVerticalCenter, ACRVerticalBottom }; + +typedef NS_ENUM(NSUInteger, ACRHeightType) { + ACRHeightAuto = 0, + ACRHeightStretch +}; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h index 273f78c401..d0eaa8bfb3 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h @@ -14,7 +14,7 @@ @property (nonatomic) BOOL hasPadding; -- (UIView *)configurePaddingFor:(UIView *)view; +- (void)configurePaddingFor:(UIView *)view; - (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm index 4976ef4049..ea6d5bca21 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm @@ -32,9 +32,9 @@ - (BOOL)hasPadding return NO; } -- (UIView *)configurePaddingFor:(UIView *)view +- (void)configurePaddingFor:(UIView *)view { - UIView *blankTrailingSpace = [[UIView alloc] init]; + UIView *blankTrailingSpace = view; //[[UIView alloc] init]; blankTrailingSpace.translatesAutoresizingMaskIntoConstraints = NO; [blankTrailingSpace setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; NSMutableArray *values = [_paddingMap objectForKey:view]; @@ -45,7 +45,7 @@ - (UIView *)configurePaddingFor:(UIView *)view [values addObject:[NSValue valueWithNonretainedObject:blankTrailingSpace]]; [_paddingSet addObject:blankTrailingSpace]; - return blankTrailingSpace; + //return blankTrailingSpace; } - (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement @@ -53,7 +53,8 @@ - (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCard UIView *blankTrailingSpace = nil; if (HeightType::Stretch == correspondingElement.element->GetHeight()) { - blankTrailingSpace = [self configurePaddingFor:view]; + [self configurePaddingFor:view]; + [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; } return blankTrailingSpace; @@ -63,7 +64,7 @@ - (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCard { if (_paddingSet.count > 1) { NSMutableArray *constraints = [[NSMutableArray alloc] init]; - UIView *prevPadding = nil; + UIView *prevPadding = nil; for (NSArray *values in _paddingMap.objectEnumerator) { for (NSValue *value in values) { UIView *padding = [value nonretainedObjectValue]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm index bb78d86bbc..4c6ea780b6 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm @@ -64,8 +64,8 @@ - (UIView *)render:(UIView *)viewGroup UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { - leadingBlankSpace = [column configurePaddingFor:column]; - [column addArrangedSubview:leadingBlankSpace]; +// leadingBlankSpace = [column configurePaddingFor:column]; +// [column addArrangedSubview:leadingBlankSpace]; } ACRColumnSetView *columnsetView = (ACRColumnSetView *)viewGroup; @@ -79,13 +79,13 @@ - (UIView *)render:(UIView *)viewGroup andHostConfig:acoConfig]; if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Top && _fillAlignment)) { - trailingBlankSpace = [column configurePaddingFor:column]; - [column addArrangedSubview:trailingBlankSpace]; +// trailingBlankSpace = [column configurePaddingFor:column]; +// [column addArrangedSubview:trailingBlankSpace]; } if (!column.hasStretchableView) { - [column addArrangedSubview:[column configurePaddingFor:column]]; - column.hasPaddingView = YES; +// [column addArrangedSubview:[column configurePaddingFor:column]]; +// column.hasPaddingView = YES; } if (columnElem->GetMinHeight() > 0) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index 088d8f043f..37da72c6ab 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { @property NSMutableArray *inputHandlers; @property (weak) ACRColumnSetView *columnsetView; -- (UIView *)configurePaddingFor:(UIView *)view; +- (void)configurePaddingFor:(UIView *)view; - (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index f4dd4ad08a..a4e4cb5cf3 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -137,9 +137,9 @@ - (UIView *)addPaddingSpace return padding; } -- (UIView *)configurePaddingFor:(UIView *)view +- (void)configurePaddingFor:(UIView *)view { - return [_paddingHandler configurePaddingFor:view]; + [_paddingHandler configurePaddingFor:view]; } - (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index e1604ee68f..540360f069 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -65,8 +65,8 @@ - (UIView *)render:(UIView *)viewGroup UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; if (containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { - leadingBlankSpace = [container configurePaddingFor:container]; - [container addArrangedSubview:leadingBlankSpace]; +// leadingBlankSpace = [container configurePaddingFor:container]; +// [container addArrangedSubview:leadingBlankSpace]; } container.frame = viewGroup.frame; @@ -80,8 +80,8 @@ - (UIView *)render:(UIView *)viewGroup const VerticalContentAlignment adaptiveVAlignment = containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top); // Dont add the trailing space if the vertical content alignment is top/default if (adaptiveVAlignment == VerticalContentAlignment::Center || (adaptiveVAlignment == VerticalContentAlignment::Top && !(container.hasStretchableView))) { - trailingBlankSpace = [container configurePaddingFor:container]; - [container addArrangedSubview:trailingBlankSpace]; +// trailingBlankSpace = [container configurePaddingFor:container]; +// [container addArrangedSubview:trailingBlankSpace]; } [container setClipsToBounds:NO]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm index fc234d278f..1e89c81238 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm @@ -203,7 +203,9 @@ - (void)update:(ACRImageProperties *)imageProperties - (void)setHeightConstraint { [self updateIntrinsicContentSizeOfSelfAndViewGroup]; - heightConstraint = [self setHeightConstraintUtil:self.heightAnchor]; + if (self.imageProperties.height != ACRHeightStretch) { + heightConstraint = [self setHeightConstraintUtil:self.heightAnchor]; + } } - (void)setImageViewHeightConstraint diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.h index 4d3c771157..3f0e1da60d 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.h @@ -18,6 +18,7 @@ @property CGSize contentSize; @property ACRImageSize acrImageSize; @property ACRHorizontalAlignment acrHorizontalAlignment; +@property ACRHeightType height; @property CGFloat pixelWidth; @property CGFloat pixelHeight; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm index f7607f72a4..40296a3ebb 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm @@ -27,6 +27,7 @@ - (instancetype)init:(ACOBaseCardElement *)acoElem config:(ACOHostConfig *)acoCo if (!acoElem or !acoConfig) { self.contentSize = CGSizeZero; self.acrImageSize = ACRImageSizeAuto; + self.height = ACRHeightAuto; self.acrHorizontalAlignment = ACRLeft; } else { std::shared_ptr elem = [acoElem element]; @@ -48,6 +49,8 @@ - (instancetype)init:(ACOBaseCardElement *)acoElem config:(ACOHostConfig *)acoCo [self updateContentSize:image.size]; } self.acrHorizontalAlignment = getACRHorizontalAlignment(imgElem->GetHorizontalAlignment().value_or(HorizontalAlignment::Left)); + + self.height = GetACRHeight(imgElem->GetHeight()); } } return self; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index 9fe4791960..21db01031f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -95,8 +95,12 @@ - (UIView *)render:(UIView *)viewGroup default: break; } - - [wrappingView.heightAnchor constraintEqualToAnchor:view.heightAnchor].active = YES; + + if (imgElem->GetHeight() == HeightType::Auto) { + [wrappingView.heightAnchor constraintEqualToAnchor:view.heightAnchor].active = YES; + } else { + [wrappingView.heightAnchor constraintGreaterThanOrEqualToAnchor:view.heightAnchor].active = YES; + } [wrappingView.widthAnchor constraintGreaterThanOrEqualToAnchor:view.widthAnchor].active = YES; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h index c75c50aa4d..2581aba634 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h @@ -111,6 +111,8 @@ ACRImageSize getACRImageSize(ImageSize adaptiveImageSize, BOOL hasExplicitDimens ACRHorizontalAlignment getACRHorizontalAlignment(HorizontalAlignment horizontalAlignment); +ACRHeightType GetACRHeight(HeightType adaptiveHeight); + void printSize(NSString *msg, CGSize size); NSData *JsonToNSData(const Json::Value &blob); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm index be6ce71b63..39ca1109cd 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm @@ -921,6 +921,20 @@ ACRHorizontalAlignment getACRHorizontalAlignment(HorizontalAlignment horizontalA } } +ACRHeightType GetACRHeight(HeightType adaptiveHeight) +{ + ACRHeightType height = ACRHeightAuto; + switch (adaptiveHeight) { + case AdaptiveCards::HeightType::Auto: + height = ACRHeightAuto; + break; + case AdaptiveCards::HeightType::Stretch: + height = ACRHeightStretch; + break; + } + return height; +} + void printSize(NSString *msg, CGSize size) { NSLog(@"%@, size = %f x %f", msg, size.width, size.height); From 69a4c650bf0572da24e9055bf5de6e7e2df544de Mon Sep 17 00:00:00 2001 From: nesalang Date: Tue, 31 Aug 2021 14:52:08 -0700 Subject: [PATCH 05/14] mostly done need to refactor and update padding management code --- samples/v1.0/Elements/Action.OpenUrl.json | 166 ++---------------- .../AdaptiveCards/AdaptiveCards/ACOEnums.h | 8 +- .../AdaptiveCards/ACOPaddingHandler.h | 4 +- .../AdaptiveCards/ACOPaddingHandler.mm | 73 ++++---- .../AdaptiveCards/ACORenderContext.h | 2 +- .../AdaptiveCards/ACORenderContext.mm | 6 +- .../AdaptiveCards/ACRColumnRenderer.mm | 34 +--- .../AdaptiveCards/ACRColumnSetRenderer.mm | 14 +- .../AdaptiveCards/ACRColumnView.h | 10 +- .../AdaptiveCards/ACRColumnView.mm | 53 ++++-- .../AdaptiveCards/ACRContainerRenderer.mm | 34 +--- .../AdaptiveCards/ACRContentHoldingUIView.mm | 5 +- .../AdaptiveCards/ACRFactSetRenderer.mm | 10 ++ .../AdaptiveCards/ACRImageRenderer.mm | 17 +- .../AdaptiveCards/ACRInputLabelView.mm | 8 + .../AdaptiveCards/ACRInputRenderer.mm | 2 +- .../AdaptiveCards/ACRRenderer.mm | 31 +--- .../AdaptiveCards/ACRTableCellView.h | 2 +- .../AdaptiveCards/ACRTableCellView.mm | 6 +- .../AdaptiveCards/ACRTableRow.mm | 2 +- .../AdaptiveCards/AdaptiveCards/UtiliOS.h | 2 + .../AdaptiveCards/AdaptiveCards/UtiliOS.mm | 17 ++ .../AdaptiveCardsColumnTests.mm | 6 +- 23 files changed, 198 insertions(+), 314 deletions(-) diff --git a/samples/v1.0/Elements/Action.OpenUrl.json b/samples/v1.0/Elements/Action.OpenUrl.json index 5b47c851a7..405190f545 100644 --- a/samples/v1.0/Elements/Action.OpenUrl.json +++ b/samples/v1.0/Elements/Action.OpenUrl.json @@ -2,162 +2,26 @@ "type": "AdaptiveCard", "body": [ { - "columns": [ - { - "width": "40", - "items": [ - { - "columns": [ - { - "width": "auto", - "items": [ - { - "color": "accent", - "text": "Requested", - "id": "approvalStatus", - "spacing": "padding", - "type": "TextBlock" - } - ], - "backgroundImage": { - "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAAhSURBVDiNY3z69e1/BjIAEzmaRjWOahzVOKpxVONw0AgAMwoD/kUfY7QAAAAASUVORK5CYII=", - "fillMode": "repeatHorizontally", - "verticalAlignment": "center" - }, - "verticalContentAlignment": "center", - "minHeight": "28px", - "spacing": "padding", - "bleed": false, - "type": "Column" - }, - { - "width": "auto", - "items": [ - - ], - "type": "Column" - } - ], - "type": "ColumnSet" - } - ], - "verticalContentAlignment": "center", - "type": "Column" - } - ], - "id": "infoTag", - "type": "ColumnSet" - }, - { - "maxLines": 2, - "size": "large", - "text": "Test", - "weight": "bolder", + "type": "TextBlock", + "text": "Hey", "wrap": true, - "id": "approvalTitle", - "spacing": "none", - "type": "TextBlock" - }, - { - "items": [ - { - "columns": [ - { - "width": "120px", - "items": [ - { - "maxLines": 5, - "text": "Requested by", - "weight": "bolder", - "wrap": true, - "type": "TextBlock" - } - ], - "type": "Column" - }, - { - "width": "stretch", - "items": [ - { - "maxLines": 5, - "text": "Isaiah Langer", - "wrap": true, - "type": "TextBlock" - } - ], - "type": "Column" - } - ], - "spacing": "none", - "type": "ColumnSet" - }, - { - "columns": [ - { - "width": "120px", - "items": [ - { - "maxLines": 5, - "text": "Pending response", - "weight": "bolder", - "wrap": true, - "type": "TextBlock" - } - ], - "type": "Column" - }, - { - "width": "stretch", - "items": [ - { - "maxLines": 5, - "text": "Isaiah Langer", - "wrap": true, - "type": "TextBlock" - } - ], - "type": "Column" - } - ], - "spacing": "none", - "type": "ColumnSet" - } - ], - "id": "aggregatedActions", - "type": "Container" + "horizontalAlignment": "Right", + "weight": "Bolder", + "size": "ExtraLarge", + "color": "Good" }, { - "actions": [ - { - "data": { - "msteams": { - "type": "task/fetch", - "title": "View Details Click" - }, - "approvalId": "611de48c-63c0-409a-8736-366d0b777b1a", - "approvalType": "Basic" - }, - "title": "View details", - "type": "Action.Submit" - } - ], - "spacing": "Large", - "type": "ActionSet" + "type": "TextBlock", + "text": "Hlo", + "wrap": true, + "horizontalAlignment": "Right", + "weight": "Bolder", + "size": "ExtraLarge", + "color": "Good" } ], "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "version": "1.2", - "refresh": { - "userIds": [ - "8:orgid:ddb1414c-0459-4dcf-9d2c-b8fd5f0f84c9" - ], - "action": { - "verb": "AutoRefresh", - "data": { - "approvalId": "611de48c-63c0-409a-8736-366d0b777b1a" - }, - "title": "AutoRefresh", - "type": "Action.Execute" - } - } + "minHeight": "400px", + "verticalContentAlignment": "center" } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h index 0a19a352a1..53c2914def 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOEnums.h @@ -81,10 +81,10 @@ typedef NS_ENUM(NSUInteger, ACRHorizontalAlignment) { }; -typedef NS_ENUM(NSUInteger, ACRVerticalAlignment) { - ACRVerticalTop = 0, - ACRVerticalCenter, - ACRVerticalBottom +typedef NS_ENUM(NSUInteger, ACRVerticalContentAlignment) { + ACRVerticalContentAlignmentTop = 0, + ACRVerticalContentAlignmentCenter, + ACRVerticalContentAlignmentBottom }; typedef NS_ENUM(NSUInteger, ACRHeightType) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h index d0eaa8bfb3..04716e025f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h @@ -14,9 +14,9 @@ @property (nonatomic) BOOL hasPadding; -- (void)configurePaddingFor:(UIView *)view; +- (void)addPaddingFor:(UIView *)view; -- (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; +- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; - (NSArray *)activateConstraintsForPadding; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm index ea6d5bca21..a69c8424a1 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm @@ -11,7 +11,7 @@ @implementation ACOPaddingHandler { NSMapTable *> *_paddingMap; - NSHashTable *_paddingSet; + NSHashTable *_stretchableViewSet; } - (instancetype)init @@ -19,64 +19,73 @@ - (instancetype)init self = [super init]; if (self) { _paddingMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory]; - _paddingSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; + _stretchableViewSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; } return self; } - (BOOL)hasPadding { - if (_paddingSet) { - return _paddingSet.count != 0; + if (_stretchableViewSet) { + return _stretchableViewSet.count != 0; } return NO; } -- (void)configurePaddingFor:(UIView *)view -{ - UIView *blankTrailingSpace = view; //[[UIView alloc] init]; - blankTrailingSpace.translatesAutoresizingMaskIntoConstraints = NO; - [blankTrailingSpace setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; +- (void)addPaddingFor:(UIView *)view { + if (!view) { + return; + } + + UIView *padding = [[UIView alloc] init]; + + [self configureHugging:padding]; + NSMutableArray *values = [_paddingMap objectForKey:view]; if (!values) { values = [[NSMutableArray alloc] init]; [_paddingMap setObject:values forKey:view]; } - [values addObject:[NSValue valueWithNonretainedObject:blankTrailingSpace]]; - [_paddingSet addObject:blankTrailingSpace]; - //return blankTrailingSpace; + [values addObject:[NSValue valueWithNonretainedObject:padding]]; } -- (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement -{ - UIView *blankTrailingSpace = nil; +- (void)configureHugging:(UIView *)view { + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; +} - if (HeightType::Stretch == correspondingElement.element->GetHeight()) { - [self configurePaddingFor:view]; - [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; +- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement +{ + if (!view || !correspondingElement || !correspondingElement.element) { + return; + } + + if ((HeightType::Stretch == correspondingElement.element->GetHeight()) && + (correspondingElement.type != ACRImage)) { + [self configureHugging:view]; + [_stretchableViewSet addObject:view]; } - - return blankTrailingSpace; } - (NSArray *)activateConstraintsForPadding { - if (_paddingSet.count > 1) { + if (_stretchableViewSet.count > 1) { NSMutableArray *constraints = [[NSMutableArray alloc] init]; UIView *prevPadding = nil; - for (NSArray *values in _paddingMap.objectEnumerator) { - for (NSValue *value in values) { - UIView *padding = [value nonretainedObjectValue]; - if (prevPadding && padding) { - [constraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; - constraints.lastObject.priority = UILayoutPriorityDefaultLow; - } - - prevPadding = padding; + for (UIView *padding in _stretchableViewSet.objectEnumerator) { + if (prevPadding) { + [constraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; + constraints.lastObject.priority = UILayoutPriorityDefaultLow; + } + prevPadding = padding; + } + + if (constraints && constraints.count) { + [NSLayoutConstraint activateConstraints:constraints]; } - [NSLayoutConstraint activateConstraints:constraints]; + return constraints; } return nil; @@ -84,7 +93,7 @@ - (UIView *)configurePaddingFor:(UIView *)view correspondingElement:(ACOBaseCard - (BOOL)isPadding:(UIView *)padding { - return [_paddingSet containsObject:padding]; + return [_stretchableViewSet containsObject:padding]; } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.h index 129a4eed94..e4210f3861 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.h @@ -19,7 +19,7 @@ @property (readonly) BOOL hasSelectAction; @property (readonly) BOOL allHasActionIcons; @property (readonly) BOOL isFirstRowAsHeaders; -@property (readonly) ACRVerticalAlignment verticalContentAlignment; +@property (readonly) ACRVerticalContentAlignment verticalContentAlignment; @property (readonly) ACRHorizontalAlignment horizontalContentAlignment; @property (weak) ACOHostConfig* _Nullable hostConfig; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.mm index ed90d6d2f9..f9ff578f23 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACORenderContext.mm @@ -111,15 +111,15 @@ - (BOOL)isFirstRowAsHeaders return NO; } -- (ACRVerticalAlignment)verticalContentAlignment +- (ACRVerticalContentAlignment)verticalContentAlignment { if (_verticalAlignmentContext && [_verticalAlignmentContext count]) { NSNumber *number = [_verticalAlignmentContext lastObject]; if (number) { - return (ACRVerticalAlignment)[number intValue]; + return (ACRVerticalContentAlignment)[number intValue]; } } - return ACRVerticalTop; + return ACRVerticalContentAlignmentTop; } - (ACRHorizontalAlignment)horizontalContentAlignment diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm index 4c6ea780b6..5984af1505 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm @@ -62,12 +62,6 @@ - (UIView *)render:(UIView *)viewGroup } } - UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; - if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { -// leadingBlankSpace = [column configurePaddingFor:column]; -// [column addArrangedSubview:leadingBlankSpace]; - } - ACRColumnSetView *columnsetView = (ACRColumnSetView *)viewGroup; column.isLastColumn = columnsetView.isLastColumn; column.columnsetView = columnsetView; @@ -78,28 +72,10 @@ - (UIView *)render:(UIView *)viewGroup withCardElems:columnElem->GetItems() andHostConfig:acoConfig]; - if (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || (columnElem->GetVerticalContentAlignment() == VerticalContentAlignment::Top && _fillAlignment)) { -// trailingBlankSpace = [column configurePaddingFor:column]; -// [column addArrangedSubview:trailingBlankSpace]; - } - - if (!column.hasStretchableView) { -// [column addArrangedSubview:[column configurePaddingFor:column]]; -// column.hasPaddingView = YES; - } - - if (columnElem->GetMinHeight() > 0) { - NSLayoutConstraint *constraint = - [NSLayoutConstraint constraintWithItem:column - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:columnElem->GetMinHeight()]; - constraint.priority = 999; - constraint.active = YES; - } + [column configureHeight:GetACRVerticalContentAlignment(columnElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) + minHeight:columnElem->GetMinHeight() + heightType:GetACRHeight(columnElem->GetHeight()) + type:ACRColumn]; [column setClipsToBounds:NO]; @@ -110,8 +86,6 @@ - (UIView *)render:(UIView *)viewGroup column.shouldGroupAccessibilityChildren = YES; - [column activatePaddingConstraints]; - // config visibility for column view followed by configuring visibility of the items of column configVisibility(column, elem); configVisibilityWithVisibilityManager(rootView, column, column); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm index bf6fc611da..f92703bc65 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm @@ -190,9 +190,9 @@ - (UIView *)render:(UIView *)viewGroup [columnSetView setAlignmentForColumnStretch]; } - if (curView.hasPaddingView) { - [viewsWithPaddingView addObject:curView]; - } +// if (curView.hasPaddingView) { +// [viewsWithPaddingView addObject:curView]; +// } CGSize size = [curView intrinsicContentSize]; if (size.width * size.height > maxIntrinsicSize) { @@ -203,10 +203,10 @@ - (UIView *)render:(UIView *)viewGroup prevColumn = column; } - if (columns.size() > 1 && [viewsWithPaddingView containsObject:viewWithMaxSize]) { - viewWithMaxSize.hasPaddingView = NO; - [viewWithMaxSize removeLastViewFromArrangedSubview]; - } +// if (columns.size() > 1 && [viewsWithPaddingView containsObject:viewWithMaxSize]) { +// viewWithMaxSize.hasPaddingView = NO; +// [viewWithMaxSize removeLastViewFromArrangedSubview]; +// } for (ACRColumnView *view in viewsWithRelativeWidth) { if (view != viewWithMinWidth && view.relativeWidth) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index 37da72c6ab..bfc2599020 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -20,17 +20,21 @@ typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { @property (nonatomic) NSString *columnWidth; @property CGFloat pixelWidth; @property CGFloat relativeWidth; +@property ACRHeightType heightType; @property BOOL hasMoreThanOneRelativeWidth; @property (nonatomic) BOOL hasStretchableView; -@property BOOL hasPaddingView; @property BOOL isLastColumn; @property NSMutableArray *inputHandlers; @property (weak) ACRColumnSetView *columnsetView; - (void)configurePaddingFor:(UIView *)view; -- (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element; +- (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element; + +- (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment + minHeight:(NSInteger)minHeight + heightType:(ACRHeightType)heightType + type:(ACRCardElementType)type; -- (void)activatePaddingConstraints; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index a4e4cb5cf3..f082d22bfe 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -5,9 +5,10 @@ // Copyright © 2020 Microsoft. All rights reserved. // -#import "ACRColumnView.h" -#import "ACOVisibilityManager.h" #import "ACOPaddingHandler.h" +#import "ACOVisibilityManager.h" +#import "ACOBaseCardElementPrivate.h" +#import "ACRView.h" @implementation ACRColumnView { ACOVisibilityManager *_visibilityManager; @@ -126,7 +127,8 @@ - (void)unhideView:(UIView *)view [_visibilityManager unhideView:view arrangedSubviews:[self getContentStackSubviews]]; } -- (BOOL)hasStretchableView { +- (BOOL)hasStretchableView +{ return _paddingHandler.hasPadding; } @@ -139,18 +141,49 @@ - (UIView *)addPaddingSpace - (void)configurePaddingFor:(UIView *)view { - [_paddingHandler configurePaddingFor:view]; +// [_paddingHandler configureHeight:view]; } -- (UIView *)configPadding:(UIView *)view acoElement:(ACOBaseCardElement *)element +- (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element { - return [_paddingHandler configurePaddingFor:view correspondingElement:element]; -} + [_paddingHandler configureHeight:view correspondingElement:element]; +} + +- (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment + minHeight:(NSInteger)minHeight + heightType:(ACRHeightType)heightType + type:(ACRCardElementType)type +{ + if (!self.hasStretchableView) { + if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || + verticalContentAlignment == ACRVerticalContentAlignmentBottom) { + UIView *padding = [[UIView alloc] init]; + [self configurePaddingFor:padding]; + [self insertArrangedSubview:padding atIndex:0]; + } -- (void)activatePaddingConstraints -{ + if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || + (verticalContentAlignment == ACRVerticalContentAlignmentTop && + self.distribution == UIStackViewDistributionFill)) { + UIView *padding = [[UIView alloc] init]; + [self configurePaddingFor:padding]; + [self addArrangedSubview:padding]; + } + } + if (minHeight > 0) { + NSLayoutConstraint *constraint = + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1 + constant:minHeight]; + constraint.priority = 999; + constraint.active = YES; + } + [_paddingHandler activateConstraintsForPadding]; } - @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index 540360f069..0aa3c0b556 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -63,12 +63,6 @@ - (UIView *)render:(UIView *)viewGroup renderBackgroundImage(containerElem->GetBackgroundImage(), container, rootView); - UIView *leadingBlankSpace = nil, *trailingBlankSpace = nil; - if (containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Center || containerElem->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { -// leadingBlankSpace = [container configurePaddingFor:container]; -// [container addArrangedSubview:leadingBlankSpace]; - } - container.frame = viewGroup.frame; [ACRRenderer render:container @@ -77,39 +71,23 @@ - (UIView *)render:(UIView *)viewGroup withCardElems:containerElem->GetItems() andHostConfig:acoConfig]; - const VerticalContentAlignment adaptiveVAlignment = containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top); - // Dont add the trailing space if the vertical content alignment is top/default - if (adaptiveVAlignment == VerticalContentAlignment::Center || (adaptiveVAlignment == VerticalContentAlignment::Top && !(container.hasStretchableView))) { -// trailingBlankSpace = [container configurePaddingFor:container]; -// [container addArrangedSubview:trailingBlankSpace]; - } - [container setClipsToBounds:NO]; - if (containerElem->GetMinHeight() > 0) { - NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:container - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:containerElem->GetMinHeight()]; - constraint.priority = 999; - constraint.active = YES; - } - std::shared_ptr selectAction = containerElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; [container configureForSelectAction:acoSelectAction rootView:rootView]; configVisibility(container, elem); [container hideIfSubviewsAreAllHidden]; - - [container activatePaddingConstraints]; + + [container configureHeight:GetACRVerticalContentAlignment(containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) + minHeight:containerElem->GetMinHeight() + heightType:GetACRHeight(containerElem->GetHeight()) + type:ACRContainer]; [rootView.context popBaseCardElementContext:acoElem]; - return viewGroup; + return container; } - (void)configUpdateForUIImageView:(ACRView *)rootView acoElem:(ACOBaseCardElement *)acoElem config:(ACOHostConfig *)acoConfig image:(UIImage *)image imageView:(UIImageView *)imageView diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm index 1e89c81238..0ef975adca 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm @@ -203,9 +203,8 @@ - (void)update:(ACRImageProperties *)imageProperties - (void)setHeightConstraint { [self updateIntrinsicContentSizeOfSelfAndViewGroup]; - if (self.imageProperties.height != ACRHeightStretch) { - heightConstraint = [self setHeightConstraintUtil:self.heightAnchor]; - } + heightConstraint = [self setHeightConstraintUtil:self.heightAnchor]; + } - (void)setImageViewHeightConstraint diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm index 0023193015..1e94589415 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm @@ -191,6 +191,16 @@ - (UIView *)render:(UIView *)viewGroup configRtl(valueLab, rootView.context); } + if (elem->GetHeight() == HeightType::Stretch) { + if (titleStack.arrangedSubviews.count) { + [titleStack.arrangedSubviews.lastObject setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + } + + if (valueStack.arrangedSubviews.count) { + [valueStack.arrangedSubviews.lastObject setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + } + } + factSetWrapperView.accessibilityElements = accessibilityElements; if (!nValidFacts) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index 21db01031f..c23c8650c1 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -96,12 +96,15 @@ - (UIView *)render:(UIView *)viewGroup break; } - if (imgElem->GetHeight() == HeightType::Auto) { - [wrappingView.heightAnchor constraintEqualToAnchor:view.heightAnchor].active = YES; - } else { - [wrappingView.heightAnchor constraintGreaterThanOrEqualToAnchor:view.heightAnchor].active = YES; + [wrappingView.heightAnchor constraintEqualToAnchor:view.heightAnchor].active = YES; + + if (imgElem->GetHeight() == HeightType::Stretch) { + UIView *padding = [[UIView alloc] init]; + padding.translatesAutoresizingMaskIntoConstraints = NO; + [padding setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + [viewGroup addArrangedSubview:padding]; } - + [wrappingView.widthAnchor constraintGreaterThanOrEqualToAnchor:view.widthAnchor].active = YES; [view.topAnchor constraintEqualToAnchor:wrappingView.topAnchor].active = YES; @@ -119,7 +122,7 @@ - (UIView *)render:(UIView *)viewGroup [view setContentCompressionResistancePriority:imagePriority forAxis:UILayoutConstraintAxisHorizontal]; [view setContentCompressionResistancePriority:imagePriority forAxis:UILayoutConstraintAxisVertical]; } - + std::shared_ptr selectAction = imgElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; // instantiate and add tap gesture recognizer @@ -146,7 +149,7 @@ - (UIView *)render:(UIView *)viewGroup if (view && view.image) { // if we already have UIImageView and UIImage, configures the constraints and turn off the notification [self configUpdateForUIImageView:rootView acoElem:acoElem config:acoConfig image:view.image imageView:view]; - } + } return wrappingView; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm index f7f259bdaa..1b2c23fdea 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm @@ -10,6 +10,7 @@ #import "ACRIContentHoldingView.h" #import "ACRInputLabelViewPrivate.h" #import "UtiliOS.h" +#import "ACRQuickReplyView.h" @implementation ACRInputLabelView @@ -119,6 +120,13 @@ - (instancetype)initInputLabelView:(ACRView *)rootView acoConfig:(ACOHostConfig self.inputAccessibilityItem.isAccessibilityElement = YES; self.labelText = self.inputAccessibilityItem.accessibilityLabel; + + if (HeightType::Stretch == inputBlck->GetHeight() && [inputView isKindOfClass:[ACRQuickReplyView class]]) { + UIView *padding = [[UIView alloc] init]; + padding.translatesAutoresizingMaskIntoConstraints = NO; + [padding setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + [self.stack addArrangedSubview:padding]; + } self.shouldGroupAccessibilityChildren = NO; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm index 944907cc03..1b6ac90887 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm @@ -162,7 +162,7 @@ - (UIView *)render:(UIView *)viewGroup } txtInput.delegate = textInputHandler; - [inputview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; +// [inputview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; } [viewGroup addArrangedSubview:inputview]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm index 5b41dcfaed..be9d9abf23 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm @@ -97,19 +97,6 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr const &)adapt [verticalView configureForSelectAction:acoSelectAction rootView:rootView]; } - if (adaptiveCard->GetMinHeight() > 0) { - NSLayoutConstraint *constraint = - [NSLayoutConstraint constraintWithItem:rootView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:adaptiveCard->GetMinHeight()]; - constraint.priority = 999; - constraint.active = YES; - } - auto backgroundImageProperties = adaptiveCard->GetBackgroundImage(); if ((backgroundImageProperties != nullptr) && !(backgroundImageProperties->GetUrl().empty())) { ObserverActionBlock observerAction = @@ -134,13 +121,13 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr const &)adapt [rootView addBaseCardElementListToConcurrentQueue:body registration:[ACRRegistration getInstance]]; - UIView *leadingBlankSpace = nil; - if (adaptiveCard->GetVerticalContentAlignment() == VerticalContentAlignment::Center || - adaptiveCard->GetVerticalContentAlignment() == VerticalContentAlignment::Bottom) { - leadingBlankSpace = [verticalView addPaddingSpace]; - } - [ACRRenderer render:verticalView rootView:rootView inputs:inputs withCardElems:body andHostConfig:config]; + + [verticalView configureHeight:GetACRVerticalContentAlignment(adaptiveCard->GetVerticalContentAlignment()) + minHeight:adaptiveCard->GetMinHeight() + heightType:GetACRHeight(adaptiveCard->GetHeight()) + type:ACRColumn + ]; [[rootView card] setInputs:inputs]; @@ -214,12 +201,8 @@ + (UIView *)render:(UIView *)view } renderedView = [renderer render:view rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:config]; - if (columnView) { - UIView *padding = [columnView configPadding:renderedView acoElement:acoElem]; - if (padding) { - [view addArrangedSubview:padding]; - } + [columnView configureHeightFor:renderedView acoElement:acoElem]; } if (separator && !renderedView) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.h index 02f7e354ec..35d8b5705c 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.h @@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN @interface ACRTableCellDefinition : NSObject @property ACRContainerStyle style; -@property ACRVerticalAlignment verticalAlignment; +@property ACRVerticalContentAlignment verticalAlignment; @property ACRHorizontalAlignment horizontalAlignment; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.mm index 994e12a346..ed7bc5a707 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableCellView.mm @@ -73,13 +73,13 @@ - (void)setHorizontalAlignment - (void)setVerticalAlignment { switch (_definition.verticalAlignment) { - case ACRVerticalTop: + case ACRVerticalContentAlignmentTop: [_contentView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES; break; - case ACRVerticalCenter: + case ACRVerticalContentAlignmentCenter: [_contentView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor].active = YES; break; - case ACRVerticalBottom: + case ACRVerticalContentAlignmentBottom: [_contentView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES; break; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableRow.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableRow.mm index 673320e2ec..a389f94cb7 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableRow.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRTableRow.mm @@ -95,7 +95,7 @@ - (instancetype)init:(ACOBaseCardElement *)acoElem cellDefinition.style = (ACRContainerStyle)style; cellDefinition.horizontalAlignment = (ACRHorizontalAlignment)row->GetHorizontalCellContentAlignment().value_or(static_cast(rootView.context.horizontalContentAlignment)); cellDefinition.verticalAlignment = - (ACRVerticalAlignment)cell->GetVerticalContentAlignment().value_or(row->GetVerticalCellContentAlignment().value_or(static_cast(rootView.context.verticalContentAlignment))); + (ACRVerticalContentAlignment)cell->GetVerticalContentAlignment().value_or(row->GetVerticalCellContentAlignment().value_or(static_cast(rootView.context.verticalContentAlignment))); cellView = [[ACRTableCellView alloc] init:[[ACOBaseCardElement alloc] initWithBaseCardElement:cell] cellDefinition:cellDefinition rootView:rootView diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h index 2581aba634..01e7627637 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.h @@ -113,6 +113,8 @@ ACRHorizontalAlignment getACRHorizontalAlignment(HorizontalAlignment horizontalA ACRHeightType GetACRHeight(HeightType adaptiveHeight); +ACRVerticalContentAlignment GetACRVerticalContentAlignment(VerticalContentAlignment adaptiveVerticalContentAlignment); + void printSize(NSString *msg, CGSize size); NSData *JsonToNSData(const Json::Value &blob); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm index 39ca1109cd..1027677665 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/UtiliOS.mm @@ -935,6 +935,23 @@ ACRHeightType GetACRHeight(HeightType adaptiveHeight) return height; } +ACRVerticalContentAlignment GetACRVerticalContentAlignment(VerticalContentAlignment adaptiveVerticalContentAlignment) +{ + ACRVerticalContentAlignment contentAlignment = ACRVerticalContentAlignmentTop; + switch (adaptiveVerticalContentAlignment){ + case AdaptiveCards::VerticalContentAlignment::Top: + contentAlignment = ACRVerticalContentAlignmentTop; + break; + case AdaptiveCards::VerticalContentAlignment::Center: + contentAlignment = ACRVerticalContentAlignmentCenter; + break; + case AdaptiveCards::VerticalContentAlignment::Bottom: + contentAlignment = ACRVerticalContentAlignmentBottom; + break; + } + return contentAlignment; +} + void printSize(NSString *msg, CGSize size) { NSLog(@"%@, size = %f x %f", msg, size.width, size.height); diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm index cfc7237c98..1f7b297556 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm @@ -250,11 +250,11 @@ - (void)testPaddingConfigurePadding auto elem = std::make_shared(); ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] initWithBaseCardElement:elem]; UIView *view = [[UIView alloc] init]; - UIView *padding0 = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + UIView *padding0 = [paddingHandler configureHeight:view correspondingElement:acoElem]; XCTAssertNil(padding0); elem->SetHeight(HeightType::Stretch); - UIView *padding1 = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + UIView *padding1 = [paddingHandler configureHeight:view correspondingElement:acoElem]; XCTAssertNotNil(padding1); } @@ -279,7 +279,7 @@ - (void)testPaddingActivateConstraints for (ACOBaseCardElement *acoElem in textBlocks) { UIView *view = [[UIView alloc] init]; [superView addSubview:view]; - UIView *padding = [paddingHandler configurePaddingFor:view correspondingElement:acoElem]; + UIView *padding = [paddingHandler configureHeight:view correspondingElement:acoElem]; if (padding) { [superView addSubview:padding]; } From 583175e3b2e8491176f1d1ce7664c6d51255b281 Mon Sep 17 00:00:00 2001 From: nesalang Date: Fri, 3 Sep 2021 10:30:34 -0700 Subject: [PATCH 06/14] [iOS] refactored --- .../AdaptiveCards.xcodeproj/project.pbxproj | 32 ++++++- .../AdaptiveCards/ACOPaddingHandler.h | 2 +- .../AdaptiveCards/ACOPaddingHandler.mm | 24 ++--- .../AdaptiveCards/ACRColumnView.h | 2 +- .../AdaptiveCards/ACRColumnView.mm | 21 ++--- .../AdaptiveCards/ACRContentHoldingUIView.mm | 1 - .../AdaptiveCards/ACRContentStackView.mm | 12 ++- .../AdaptiveCards/ACRImageProperties.mm | 3 +- .../AdaptiveCards/ACRImageRenderer.mm | 12 ++- .../AdaptiveCards/ACRInputLabelView.mm | 9 +- .../AdaptiveCards/ACRInputRenderer.mm | 4 +- .../AdaptiveCards/ACRRenderer.mm | 17 ++-- .../AdaptiveCardsColumnTests.mm | 87 +++++++++++++++++-- .../AdaptiveCardsTests/Mocks/MockACRView.h | 2 +- .../Container.VerticalContentAlignment.json | 33 +++++++ 15 files changed, 194 insertions(+), 67 deletions(-) create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment.json diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 6520456742..130a71ed3f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -177,8 +177,11 @@ 6BD025ED254784670009B019 /* ACOInputResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD025EB254784660009B019 /* ACOInputResults.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BD025EE254784670009B019 /* ACOInputResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD025EC254784670009B019 /* ACOInputResults.mm */; }; 6BD90D8E24C37D5900B040FB /* ACRInputLabelViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD90D8D24C37D5900B040FB /* ACRInputLabelViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */; }; + 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BDD085526D5727C00969B41 /* ACOPaddingHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */; }; + 6BE6C7A526E17132009E9171 /* ColumnSet.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */; }; + 6BE6C7A926E191AB009E9171 /* Column.VerticalAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */; }; + 6BE6C7AB26E19651009E9171 /* Container.VerticalContentAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */; }; 6BE8DFD4249C4C1B005EFE66 /* ACRToggleInputView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BE8DFD3249C4C1B005EFE66 /* ACRToggleInputView.mm */; }; 6BF339D320A6649500DA5973 /* json.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F4071C771FCCBAEF00AF4FEA /* json.h */; }; 6BF430772190DDCA0068E432 /* ACRQuickReplyView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF430752190DDCA0068E432 /* ACRQuickReplyView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -606,6 +609,9 @@ 6BD90D8D24C37D5900B040FB /* ACRInputLabelViewPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRInputLabelViewPrivate.h; sourceTree = ""; }; 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOPaddingHandler.h; sourceTree = ""; }; 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOPaddingHandler.mm; sourceTree = ""; }; + 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = ColumnSet.VerticalStretch.json; path = ../../../../../../samples/v1.1/Tests/ColumnSet.VerticalStretch.json; sourceTree = ""; }; + 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Column.VerticalAlignment.json; path = ../../../../../../samples/v1.1/Tests/Column.VerticalAlignment.json; sourceTree = ""; }; + 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = Container.VerticalContentAlignment.json; path = ../../../../../../samples/v1.1/Elements/Container.VerticalContentAlignment.json; sourceTree = ""; }; 6BE8DFD3249C4C1B005EFE66 /* ACRToggleInputView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRToggleInputView.mm; sourceTree = ""; }; 6BE8DFD5249C5126005EFE66 /* ACRToggleInputView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACRToggleInputView.h; sourceTree = ""; }; 6BF430752190DDCA0068E432 /* ACRQuickReplyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRQuickReplyView.h; sourceTree = ""; }; @@ -1057,6 +1063,23 @@ name = Media; sourceTree = ""; }; + 6BE6C79426E1560A009E9171 /* Helpers */ = { + isa = PBXGroup; + children = ( + ); + path = Helpers; + sourceTree = ""; + }; + 6BE6C79F26E17077009E9171 /* TestFiles */ = { + isa = PBXGroup; + children = ( + 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */, + 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */, + 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */, + ); + path = TestFiles; + sourceTree = ""; + }; 6BFCA139264F548600195CA7 /* TableView */ = { isa = PBXGroup; children = ( @@ -1143,6 +1166,8 @@ F423C0C21EE1FBAA00905679 /* AdaptiveCardsTests */ = { isa = PBXGroup; children = ( + 6BE6C79F26E17077009E9171 /* TestFiles */, + 6BE6C79426E1560A009E9171 /* Helpers */, 6B124CA326D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm */, 6B124C8B26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm */, 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */, @@ -1518,7 +1543,6 @@ F4D0694B205B27EA003645E4 /* ACRViewController.h in Headers */, F4FE45661F196E7B0071D9E5 /* ACRColumnView.h in Headers */, F4F6BA36204F200F003741B6 /* ACRParseWarning.h in Headers */, - 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */, F4F44B9D204A16BC00A2F24C /* ACRTextBlockRenderer.h in Headers */, F4F44B9C204A169D00A2F24C /* ACRErrors.h in Headers */, F4F44B9A204A164100A2F24C /* ACRInputToggleRenderer.h in Headers */, @@ -1660,6 +1684,7 @@ 6B124C9F26BCB2FE007E9641 /* ACOVisibilityManager.h in Headers */, 6B92A7E62677DFAB00CAE3BF /* ACRChoiceSetCompactStyleView.h in Headers */, 0D3485FD26193BA500614EB9 /* ACROverflowTarget.h in Headers */, + 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */, 0D45F5A7261731E400EF03C5 /* ACRActionOverflowRenderer.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1789,6 +1814,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6BE6C7AB26E19651009E9171 /* Container.VerticalContentAlignment.json in Resources */, + 6BE6C7A926E191AB009E9171 /* Column.VerticalAlignment.json in Resources */, + 6BE6C7A526E17132009E9171 /* ColumnSet.VerticalStretch.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h index 04716e025f..e029a58b03 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h @@ -14,7 +14,7 @@ @property (nonatomic) BOOL hasPadding; -- (void)addPaddingFor:(UIView *)view; +- (UIView *)addPaddingFor:(UIView *)view; - (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm index a69c8424a1..3348b79494 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm @@ -32,25 +32,30 @@ - (BOOL)hasPadding return NO; } -- (void)addPaddingFor:(UIView *)view { +- (UIView *)addPaddingFor:(UIView *)view +{ if (!view) { - return; + return nil; } - + UIView *padding = [[UIView alloc] init]; - + [self configureHugging:padding]; - + NSMutableArray *values = [_paddingMap objectForKey:view]; if (!values) { values = [[NSMutableArray alloc] init]; [_paddingMap setObject:values forKey:view]; } + [_stretchableViewSet addObject:padding]; [values addObject:[NSValue valueWithNonretainedObject:padding]]; + + return padding; } -- (void)configureHugging:(UIView *)view { +- (void)configureHugging:(UIView *)view +{ view.translatesAutoresizingMaskIntoConstraints = NO; [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; } @@ -60,7 +65,7 @@ - (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement if (!view || !correspondingElement || !correspondingElement.element) { return; } - + if ((HeightType::Stretch == correspondingElement.element->GetHeight()) && (correspondingElement.type != ACRImage)) { [self configureHugging:view]; @@ -77,15 +82,14 @@ - (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement if (prevPadding) { [constraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; constraints.lastObject.priority = UILayoutPriorityDefaultLow; - } prevPadding = padding; } - + if (constraints && constraints.count) { [NSLayoutConstraint activateConstraints:constraints]; } - + return constraints; } return nil; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index bfc2599020..a110e7c0bc 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -27,7 +27,7 @@ typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { @property NSMutableArray *inputHandlers; @property (weak) ACRColumnSetView *columnsetView; -- (void)configurePaddingFor:(UIView *)view; +- (UIView *)addPaddingFor:(UIView *)view; - (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index f082d22bfe..038916cd51 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -5,9 +5,8 @@ // Copyright © 2020 Microsoft. All rights reserved. // -#import "ACOPaddingHandler.h" -#import "ACOVisibilityManager.h" #import "ACOBaseCardElementPrivate.h" +#import "ACOPaddingHandler.h" #import "ACRView.h" @implementation ACRColumnView { @@ -139,9 +138,9 @@ - (UIView *)addPaddingSpace return padding; } -- (void)configurePaddingFor:(UIView *)view +- (UIView *)addPaddingFor:(UIView *)view { -// [_paddingHandler configureHeight:view]; + return [_paddingHandler addPaddingFor:view]; } - (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element @@ -152,22 +151,18 @@ - (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)eleme - (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment minHeight:(NSInteger)minHeight heightType:(ACRHeightType)heightType - type:(ACRCardElementType)type -{ + type:(ACRCardElementType)type +{ if (!self.hasStretchableView) { if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || verticalContentAlignment == ACRVerticalContentAlignmentBottom) { - UIView *padding = [[UIView alloc] init]; - [self configurePaddingFor:padding]; - [self insertArrangedSubview:padding atIndex:0]; + [self insertArrangedSubview:[self addPaddingFor:self] atIndex:0]; } if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || (verticalContentAlignment == ACRVerticalContentAlignmentTop && self.distribution == UIStackViewDistributionFill)) { - UIView *padding = [[UIView alloc] init]; - [self configurePaddingFor:padding]; - [self addArrangedSubview:padding]; + [self addArrangedSubview:[self addPaddingFor:self]]; } } if (minHeight > 0) { @@ -182,7 +177,7 @@ - (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment constraint.priority = 999; constraint.active = YES; } - + [_paddingHandler activateConstraintsForPadding]; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm index 0ef975adca..fc234d278f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm @@ -204,7 +204,6 @@ - (void)setHeightConstraint { [self updateIntrinsicContentSizeOfSelfAndViewGroup]; heightConstraint = [self setHeightConstraintUtil:self.heightAnchor]; - } - (void)setImageViewHeightConstraint diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm index ea55013bf0..ce88cd7961 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm @@ -246,14 +246,18 @@ - (void)updateIntrinsicContentSize:(void (^)(UIView *view, NSUInteger idx, BOOL - (void)addArrangedSubview:(UIView *)view { - [_stackView addArrangedSubview:view]; - [self configureVisibilityOfView:view]; + if (view) { + [_stackView addArrangedSubview:view]; + [self configureVisibilityOfView:view]; + } } - (void)insertArrangedSubview:(UIView *)view atIndex:(NSUInteger)insertionIndex { - [_stackView insertArrangedSubview:view atIndex:insertionIndex]; - [self configureVisibilityOfView:view]; + if (view) { + [_stackView insertArrangedSubview:view atIndex:insertionIndex]; + [self configureVisibilityOfView:view]; + } } - (void)configureVisibilityOfView:(UIView *)view diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm index 40296a3ebb..1a5feab65f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageProperties.mm @@ -45,11 +45,12 @@ - (instancetype)init:(ACOBaseCardElement *)acoElem config:(ACOHostConfig *)acoCo self.acrImageSize = acrImageSize; self.contentSize = [acoConfig getImageSizeAsCGSize:acrImageSize width:self.pixelWidth height:self.pixelHeight]; + if (image) { [self updateContentSize:image.size]; } + self.acrHorizontalAlignment = getACRHorizontalAlignment(imgElem->GetHorizontalAlignment().value_or(HorizontalAlignment::Left)); - self.height = GetACRHeight(imgElem->GetHeight()); } } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm index c23c8650c1..e04c256c9d 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRImageRenderer.mm @@ -95,16 +95,14 @@ - (UIView *)render:(UIView *)viewGroup default: break; } - + [wrappingView.heightAnchor constraintEqualToAnchor:view.heightAnchor].active = YES; - + + // added padding to strech for image view because stretching ImageView is not desirable if (imgElem->GetHeight() == HeightType::Stretch) { - UIView *padding = [[UIView alloc] init]; - padding.translatesAutoresizingMaskIntoConstraints = NO; - [padding setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; - [viewGroup addArrangedSubview:padding]; + [viewGroup addArrangedSubview:[(ACRColumnView *)viewGroup addPaddingFor:wrappingView]]; } - + [wrappingView.widthAnchor constraintGreaterThanOrEqualToAnchor:view.widthAnchor].active = YES; [view.topAnchor constraintEqualToAnchor:wrappingView.topAnchor].active = YES; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm index 1b2c23fdea..b2bccf6e9f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputLabelView.mm @@ -9,8 +9,8 @@ #import "ACOHostConfigPrivate.h" #import "ACRIContentHoldingView.h" #import "ACRInputLabelViewPrivate.h" -#import "UtiliOS.h" #import "ACRQuickReplyView.h" +#import "UtiliOS.h" @implementation ACRInputLabelView @@ -120,12 +120,9 @@ - (instancetype)initInputLabelView:(ACRView *)rootView acoConfig:(ACOHostConfig self.inputAccessibilityItem.isAccessibilityElement = YES; self.labelText = self.inputAccessibilityItem.accessibilityLabel; - + if (HeightType::Stretch == inputBlck->GetHeight() && [inputView isKindOfClass:[ACRQuickReplyView class]]) { - UIView *padding = [[UIView alloc] init]; - padding.translatesAutoresizingMaskIntoConstraints = NO; - [padding setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; - [self.stack addArrangedSubview:padding]; + [self.stack addArrangedSubview:[(ACRColumnView *)viewGroup addPaddingFor:self]]; } self.shouldGroupAccessibilityChildren = NO; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm index 1b6ac90887..d0d1aef3f7 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRInputRenderer.mm @@ -161,10 +161,8 @@ - (UIView *)render:(UIView *)viewGroup inputview = inputLabelView; } txtInput.delegate = textInputHandler; - -// [inputview setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; } - + [viewGroup addArrangedSubview:inputview]; inputview.translatesAutoresizingMaskIntoConstraints = false; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm index be9d9abf23..07275e2b0e 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRRenderer.mm @@ -122,12 +122,11 @@ + (UIView *)renderWithAdaptiveCards:(std::shared_ptr const &)adapt [rootView addBaseCardElementListToConcurrentQueue:body registration:[ACRRegistration getInstance]]; [ACRRenderer render:verticalView rootView:rootView inputs:inputs withCardElems:body andHostConfig:config]; - + [verticalView configureHeight:GetACRVerticalContentAlignment(adaptiveCard->GetVerticalContentAlignment()) minHeight:adaptiveCard->GetMinHeight() heightType:GetACRHeight(adaptiveCard->GetHeight()) - type:ACRColumn - ]; + type:ACRColumn]; [[rootView card] setInputs:inputs]; @@ -167,11 +166,7 @@ + (UIView *)render:(UIView *)view ACRRegistration *reg = [ACRRegistration getInstance]; ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] init]; ACOFeatureRegistration *featureReg = [ACOFeatureRegistration getInstance]; - ACRColumnView *columnView = nil; - - if ([view isKindOfClass:[ACRColumnView class]]) { - columnView = (ACRColumnView *)view; - } + UIView *renderedView = nil; auto firstelem = elems.begin(); @@ -201,8 +196,10 @@ + (UIView *)render:(UIView *)view } renderedView = [renderer render:view rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:config]; - if (columnView) { - [columnView configureHeightFor:renderedView acoElement:acoElem]; + + if ([view isKindOfClass:[ACRColumnView class]]) { + ACRColumnView *columnView = (ACRColumnView *)view; + [columnView configureHeightFor:renderedView acoElement:acoElem]; } if (separator && !renderedView) { diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm index 1f7b297556..0870e27df4 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm @@ -5,15 +5,17 @@ // Copyright © 2021 Microsoft. All rights reserved. // +#import "ACOAdaptiveCardPrivate.h" +#import "ACOBaseCardElementPrivate.h" +#import "ACOPaddingHandler.h" #import "ACORenderContext.h" #import "ACOVisibilityManager.h" -#import "ACOPaddingHandler.h" -#import "ACRColumnView.h" +#import "ACRRenderer.h" #import "ACRSeparator.h" +#import "ACRView.h" #import "Column.h" -#import "TextBlock.h" -#import "ACOBaseCardElementPrivate.h" #import "Enums.h" +#import "TextBlock.h" #import #import @@ -21,10 +23,13 @@ @interface AdaptiveCardsColumnTests : XCTestCase @end -@implementation AdaptiveCardsColumnTests +@implementation AdaptiveCardsColumnTests { + NSBundle *_mainBundle; +} - (void)setUp { + _mainBundle = [NSBundle bundleForClass:[self class]]; // Put setup code here. This method is called before the invocation of each test method in the class. } @@ -33,6 +38,18 @@ - (void)tearDown // Put teardown code here. This method is called after the invocation of each test method in the class. } +- (NSArray *)prepCards:(NSArray *)fileNames +{ + NSMutableArray *cards = [[NSMutableArray alloc] init]; + for (NSString *fileName in fileNames) { + NSString *payload = [NSString stringWithContentsOfFile:[_mainBundle pathForResource:fileName ofType:@"json"] encoding:NSUTF8StringEncoding error:nil]; + ACOAdaptiveCardParseResult *cardParseResult = [ACOAdaptiveCard fromJson:payload]; + XCTAssertTrue(cardParseResult.isValid); + [cards addObject:cardParseResult.card]; + } + return cards; +} + - (void)testVisibilityManagerSimple { UIView *viewToBeHidden0 = [[UIView alloc] init]; @@ -244,6 +261,7 @@ - (void)testPaddingInitialization XCTAssertNotNil(paddingHandler); } +#if 0 - (void)testPaddingConfigurePadding { ACOPaddingHandler *paddingHandler = [[ACOPaddingHandler alloc] init]; @@ -258,8 +276,10 @@ - (void)testPaddingConfigurePadding XCTAssertNotNil(padding1); } +#endif -NSArray *buildTextBlocksWithHeightStretch(uint n) { +NSArray *buildTextBlocksWithHeightStretch(uint n) +{ NSMutableArray *textBlocks = [[NSMutableArray alloc] init]; for (uint i = 0; i < n; i++) { auto elem0 = std::make_shared(); @@ -267,10 +287,11 @@ - (void)testPaddingConfigurePadding ACOBaseCardElement *acoElem = [[ACOBaseCardElement alloc] initWithBaseCardElement:elem0]; [textBlocks addObject:acoElem]; } - + return textBlocks; } +#if 0 - (void)testPaddingActivateConstraints { NSArray *textBlocks = buildTextBlocksWithHeightStretch(3); @@ -291,6 +312,58 @@ - (void)testPaddingActivateConstraints XCTAssertNotNil(constraints); XCTAssertTrue(constraints.count == 2); } +#endif +- (void)testPaddingOnSampleCards0 +{ + NSArray *cards = [self prepCards:@[ @"Column.VerticalAlignment" ]]; + XCTAssertNotNil(cards); + XCTAssertEqual(cards.count, 1); + ACRRenderResult *renderResult = [ACRRenderer render:cards[0] config:nil widthConstraint:320.0]; + XCTAssertEqual(renderResult.succeeded, YES); + [self dfsAt:renderResult.view + depth:1 + test:^(UIView *testView) { + if ([testView isKindOfClass:[ACRColumnSetView class]]) { + ACRColumnSetView *columnSetView = (ACRColumnSetView *)testView; + ACRColumnView *left = (ACRColumnView *)[columnSetView getArrangedSubviews][2]; + ACRColumnView *center = (ACRColumnView *)[columnSetView getArrangedSubviews][4]; + ACRColumnView *right = (ACRColumnView *)[columnSetView getArrangedSubviews][6]; + XCTAssertEqual(2, [left getArrangedSubviews].count); + XCTAssertEqual(3, [center getArrangedSubviews].count); + XCTAssertEqual(2, [right getArrangedSubviews].count); + } + }]; +} + +- (void)testPaddingOnSampleCards1 +{ + NSArray *cards = [self prepCards:@[ @"Container.VerticalContentAlignment" ]]; + + ACRRenderResult *renderResult = [ACRRenderer render:cards[0] config:nil widthConstraint:320.0]; + XCTAssertEqual(renderResult.succeeded, YES); + [self dfsAt:renderResult.view + depth:0 + test:^(UIView *testView) { + ACRColumnView *superView = (ACRColumnView *)testView; + ACRColumnView *center = (ACRColumnView *)[superView getArrangedSubviews][2]; + ACRColumnView *bottom = (ACRColumnView *)[superView getArrangedSubviews][4]; + XCTAssertEqual(3, [center getArrangedSubviews].count); + XCTAssertEqual(2, [bottom getArrangedSubviews].count); + }]; +} + +- (void)dfsAt:(UIView *)view depth:(NSUInteger)depth test:(void (^)(UIView *testView))test +{ + if (depth == 0) { + test(view); + } + + if ([view isKindOfClass:[ACRContentStackView class]]) { + for (UIView *subView in [((ACRContentStackView *)view) getArrangedSubviews]) { + [self dfsAt:subView depth:depth - 1 test:test]; + } + } +} @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/MockACRView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/MockACRView.h index 4df344f6d0..bac62dfc07 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/MockACRView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/MockACRView.h @@ -10,7 +10,7 @@ @interface MockACRView : ACRView -@property MockContext *mockContext; +@property (nonatomic) MockContext *mockContext; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment.json new file mode 100644 index 0000000000..204404b784 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.1", + "minHeight": "150px", + "body": [ + { + "type": "TextBlock", + "text": "I'm a regular TextBlock..." + }, + { + "type": "Container", + "height": "stretch", + "verticalContentAlignment": "center", + "items": [ + { + "type": "TextBlock", + "text": "But I'm centered..." + } + ] + }, + { + "type": "Container", + "verticalContentAlignment": "bottom", + "items": [ + { + "type": "TextBlock", + "text": "And I'm like a footer!" + } + ] + } + ] +} \ No newline at end of file From 756a8b17545514d14679ea862967ee6b7fcdffbd Mon Sep 17 00:00:00 2001 From: nesalang Date: Tue, 7 Sep 2021 10:26:24 -0700 Subject: [PATCH 07/14] [iOS] added padding test --- .../Container.VerticalContentAlignment.json | 8 +- .../AdaptiveCards.xcodeproj/project.pbxproj | 54 +++++++-- .../AdaptiveCards/ACRColumnView.mm | 1 + .../AdaptiveCardsColumnTests.mm | 111 ++++++++++-------- .../Helpers/ACRCustomRenderers.h | 27 +++++ .../Helpers/ACRCustomRenderers.mm | 63 ++++++++++ .../Mocks/ADCMockResolver.h | 13 ++ .../Mocks/ADCMockResolver.m | 18 +++ .../TestFiles/Column.VerticalAlignment.json | 79 +++++++++++++ .../ColumnSet.FactSet.VerticalStretch.json | 62 ++++++++++ .../ColumnSet.Image.VerticalStretch.json | 54 +++++++++ ...mnSet.Input.ChoiceSet.VerticalStretch.json | 89 ++++++++++++++ .../TestFiles/ColumnSet.VerticalStretch.json | 42 +++++++ .../Container.VerticalContentAlignment2.json | 42 +++++++ 14 files changed, 600 insertions(+), 63 deletions(-) create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.mm create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.m create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Column.VerticalAlignment.json create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.FactSet.VerticalStretch.json create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Image.VerticalStretch.json create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Input.ChoiceSet.VerticalStretch.json create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.VerticalStretch.json create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment2.json diff --git a/samples/v1.1/Elements/Container.VerticalContentAlignment.json b/samples/v1.1/Elements/Container.VerticalContentAlignment.json index 204404b784..ef126e1785 100644 --- a/samples/v1.1/Elements/Container.VerticalContentAlignment.json +++ b/samples/v1.1/Elements/Container.VerticalContentAlignment.json @@ -12,6 +12,9 @@ "type": "Container", "height": "stretch", "verticalContentAlignment": "center", + "expected": { + "numberOfPaddings" : "3" + }, "items": [ { "type": "TextBlock", @@ -22,6 +25,9 @@ { "type": "Container", "verticalContentAlignment": "bottom", + "expected": { + "numberOfPaddings" : "2" + }, "items": [ { "type": "TextBlock", @@ -30,4 +36,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 130a71ed3f..5e36e6d48f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -179,9 +179,16 @@ 6BD90D8E24C37D5900B040FB /* ACRInputLabelViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD90D8D24C37D5900B040FB /* ACRInputLabelViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BDD085526D5727C00969B41 /* ACOPaddingHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */; }; - 6BE6C7A526E17132009E9171 /* ColumnSet.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */; }; - 6BE6C7A926E191AB009E9171 /* Column.VerticalAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */; }; - 6BE6C7AB26E19651009E9171 /* Container.VerticalContentAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */; }; + 6BE6C7B026E2C9A3009E9171 /* ACRCustomRenderers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BE6C7AF26E2C9A3009E9171 /* ACRCustomRenderers.mm */; }; + 6BE6C7B226E2DF29009E9171 /* Container.VerticalContentAlignment2.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B126E2DF29009E9171 /* Container.VerticalContentAlignment2.json */; }; + 6BE6C7B626E2E140009E9171 /* Column.VerticalAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B526E2E140009E9171 /* Column.VerticalAlignment.json */; }; + 6BE6C7BA26E2E30A009E9171 /* Container.VerticalContentAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B726E2E30A009E9171 /* Container.VerticalContentAlignment.json */; }; + 6BE6C7BB26E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B826E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json */; }; + 6BE6C7BC26E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B926E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json */; }; + 6BE6C7BE26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7BD26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json */; }; + 6BE6C7C126E2ECEA009E9171 /* ADCMockResolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BE6C7BF26E2ECEA009E9171 /* ADCMockResolver.m */; }; + 6BE6C7C326E2F07C009E9171 /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7C226E2F07C009E9171 /* sample.json */; }; + 6BE6C7C526E7CDD0009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7C426E7CDCF009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json */; }; 6BE8DFD4249C4C1B005EFE66 /* ACRToggleInputView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BE8DFD3249C4C1B005EFE66 /* ACRToggleInputView.mm */; }; 6BF339D320A6649500DA5973 /* json.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = F4071C771FCCBAEF00AF4FEA /* json.h */; }; 6BF430772190DDCA0068E432 /* ACRQuickReplyView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BF430752190DDCA0068E432 /* ACRQuickReplyView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -610,8 +617,18 @@ 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOPaddingHandler.h; sourceTree = ""; }; 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOPaddingHandler.mm; sourceTree = ""; }; 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = ColumnSet.VerticalStretch.json; path = ../../../../../../samples/v1.1/Tests/ColumnSet.VerticalStretch.json; sourceTree = ""; }; - 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Column.VerticalAlignment.json; path = ../../../../../../samples/v1.1/Tests/Column.VerticalAlignment.json; sourceTree = ""; }; - 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = Container.VerticalContentAlignment.json; path = ../../../../../../samples/v1.1/Elements/Container.VerticalContentAlignment.json; sourceTree = ""; }; + 6BE6C7AE26E2C969009E9171 /* ACRCustomRenderers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACRCustomRenderers.h; sourceTree = ""; }; + 6BE6C7AF26E2C9A3009E9171 /* ACRCustomRenderers.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRCustomRenderers.mm; sourceTree = ""; }; + 6BE6C7B126E2DF29009E9171 /* Container.VerticalContentAlignment2.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Container.VerticalContentAlignment2.json; sourceTree = ""; }; + 6BE6C7B526E2E140009E9171 /* Column.VerticalAlignment.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Column.VerticalAlignment.json; sourceTree = ""; }; + 6BE6C7B726E2E30A009E9171 /* Container.VerticalContentAlignment.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Container.VerticalContentAlignment.json; sourceTree = ""; }; + 6BE6C7B826E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ColumnSet.Image.VerticalStretch.json; sourceTree = ""; }; + 6BE6C7B926E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ColumnSet.FactSet.VerticalStretch.json; sourceTree = ""; }; + 6BE6C7BD26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ColumnSet.VerticalStretch.json; sourceTree = ""; }; + 6BE6C7BF26E2ECEA009E9171 /* ADCMockResolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ADCMockResolver.m; sourceTree = ""; }; + 6BE6C7C026E2ECEA009E9171 /* ADCMockResolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADCMockResolver.h; sourceTree = ""; }; + 6BE6C7C226E2F07C009E9171 /* sample.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = sample.json; path = ../../../ADCIOSVisualizer/resources/sample.json; sourceTree = ""; }; + 6BE6C7C426E7CDCF009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = ColumnSet.Input.ChoiceSet.VerticalStretch.json; sourceTree = ""; }; 6BE8DFD3249C4C1B005EFE66 /* ACRToggleInputView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRToggleInputView.mm; sourceTree = ""; }; 6BE8DFD5249C5126005EFE66 /* ACRToggleInputView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACRToggleInputView.h; sourceTree = ""; }; 6BF430752190DDCA0068E432 /* ACRQuickReplyView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRQuickReplyView.h; sourceTree = ""; }; @@ -889,6 +906,8 @@ 6B124C9226B8AE3B007E9641 /* Mocks */ = { isa = PBXGroup; children = ( + 6BE6C7C026E2ECEA009E9171 /* ADCMockResolver.h */, + 6BE6C7BF26E2ECEA009E9171 /* ADCMockResolver.m */, 6B124C8F26B8AC37007E9641 /* MockACRView.mm */, 6B124C9126B8AC58007E9641 /* MockACRView.h */, 6B124C9326B8AE59007E9641 /* MockContext.h */, @@ -1066,6 +1085,8 @@ 6BE6C79426E1560A009E9171 /* Helpers */ = { isa = PBXGroup; children = ( + 6BE6C7AE26E2C969009E9171 /* ACRCustomRenderers.h */, + 6BE6C7AF26E2C9A3009E9171 /* ACRCustomRenderers.mm */, ); path = Helpers; sourceTree = ""; @@ -1073,9 +1094,15 @@ 6BE6C79F26E17077009E9171 /* TestFiles */ = { isa = PBXGroup; children = ( - 6BE6C7AA26E19651009E9171 /* Container.VerticalContentAlignment.json */, - 6BE6C7A826E191AB009E9171 /* Column.VerticalAlignment.json */, + 6BE6C7B526E2E140009E9171 /* Column.VerticalAlignment.json */, + 6BE6C7B926E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json */, + 6BE6C7B826E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json */, + 6BE6C7C426E7CDCF009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json */, + 6BE6C7BD26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json */, 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */, + 6BE6C7B726E2E30A009E9171 /* Container.VerticalContentAlignment.json */, + 6BE6C7B126E2DF29009E9171 /* Container.VerticalContentAlignment2.json */, + 6BE6C7C226E2F07C009E9171 /* sample.json */, ); path = TestFiles; sourceTree = ""; @@ -1814,9 +1841,14 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6BE6C7AB26E19651009E9171 /* Container.VerticalContentAlignment.json in Resources */, - 6BE6C7A926E191AB009E9171 /* Column.VerticalAlignment.json in Resources */, - 6BE6C7A526E17132009E9171 /* ColumnSet.VerticalStretch.json in Resources */, + 6BE6C7C526E7CDD0009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json in Resources */, + 6BE6C7BA26E2E30A009E9171 /* Container.VerticalContentAlignment.json in Resources */, + 6BE6C7BE26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json in Resources */, + 6BE6C7BB26E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json in Resources */, + 6BE6C7C326E2F07C009E9171 /* sample.json in Resources */, + 6BE6C7BC26E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json in Resources */, + 6BE6C7B626E2E140009E9171 /* Column.VerticalAlignment.json in Resources */, + 6BE6C7B226E2DF29009E9171 /* Container.VerticalContentAlignment2.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2014,7 +2046,9 @@ buildActionMask = 2147483647; files = ( 6B124C8C26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm in Sources */, + 6BE6C7C126E2ECEA009E9171 /* ADCMockResolver.m in Sources */, 6B124CA426D04CA9007E9641 /* AdaptiveCardsUtiliOSTest.mm in Sources */, + 6BE6C7B026E2C9A3009E9171 /* ACRCustomRenderers.mm in Sources */, 30D56DEF2682AB9C00D6E418 /* AdaptiveCardsTextBlockTests.mm in Sources */, 6B124C9526B8AE72007E9641 /* MockContext.mm in Sources */, 30D56DE9268298B300D6E418 /* AdaptiveCardsTests.mm in Sources */, diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index 038916cd51..e25ec1aea4 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -11,6 +11,7 @@ @implementation ACRColumnView { ACOVisibilityManager *_visibilityManager; +@protected ACOPaddingHandler *_paddingHandler; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm index 0870e27df4..515f69dc6b 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/AdaptiveCardsColumnTests.mm @@ -7,11 +7,14 @@ #import "ACOAdaptiveCardPrivate.h" #import "ACOBaseCardElementPrivate.h" +#import "ACRRegistration.h" +#import "ACRCustomRenderers.h" #import "ACOPaddingHandler.h" #import "ACORenderContext.h" #import "ACOVisibilityManager.h" #import "ACRRenderer.h" #import "ACRSeparator.h" +#import "Mocks/ADCMockResolver.h" #import "ACRView.h" #import "Column.h" #import "Enums.h" @@ -24,25 +27,33 @@ @interface AdaptiveCardsColumnTests : XCTestCase @end @implementation AdaptiveCardsColumnTests { - NSBundle *_mainBundle; + NSBundle *_classBundle; + NSString *_hostConfig; + NSString *_tearDownMessage; } - (void)setUp { - _mainBundle = [NSBundle bundleForClass:[self class]]; + _classBundle = [NSBundle bundleForClass:[self class]]; + _hostConfig = [NSString stringWithContentsOfFile:[_classBundle pathForResource:@"sample" ofType:@"json"] + encoding:NSUTF8StringEncoding + error:nil]; // Put setup code here. This method is called before the invocation of each test method in the class. } - (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. + if (self.testRun.failureCount && _tearDownMessage) { + NSLog(@"%@", _tearDownMessage); + _tearDownMessage = nil; + } } - (NSArray *)prepCards:(NSArray *)fileNames { NSMutableArray *cards = [[NSMutableArray alloc] init]; for (NSString *fileName in fileNames) { - NSString *payload = [NSString stringWithContentsOfFile:[_mainBundle pathForResource:fileName ofType:@"json"] encoding:NSUTF8StringEncoding error:nil]; + NSString *payload = [NSString stringWithContentsOfFile:[_classBundle pathForResource:fileName ofType:@"json"] encoding:NSUTF8StringEncoding error:nil]; ACOAdaptiveCardParseResult *cardParseResult = [ACOAdaptiveCard fromJson:payload]; XCTAssertTrue(cardParseResult.isValid); [cards addObject:cardParseResult.card]; @@ -314,56 +325,52 @@ - (void)testPaddingActivateConstraints } #endif -- (void)testPaddingOnSampleCards0 +- (void)testPaddingOnSampleCards { - NSArray *cards = [self prepCards:@[ @"Column.VerticalAlignment" ]]; - XCTAssertNotNil(cards); - XCTAssertEqual(cards.count, 1); - ACRRenderResult *renderResult = [ACRRenderer render:cards[0] config:nil widthConstraint:320.0]; - XCTAssertEqual(renderResult.succeeded, YES); - [self dfsAt:renderResult.view - depth:1 - test:^(UIView *testView) { - if ([testView isKindOfClass:[ACRColumnSetView class]]) { - ACRColumnSetView *columnSetView = (ACRColumnSetView *)testView; - ACRColumnView *left = (ACRColumnView *)[columnSetView getArrangedSubviews][2]; - ACRColumnView *center = (ACRColumnView *)[columnSetView getArrangedSubviews][4]; - ACRColumnView *right = (ACRColumnView *)[columnSetView getArrangedSubviews][6]; - XCTAssertEqual(2, [left getArrangedSubviews].count); - XCTAssertEqual(3, [center getArrangedSubviews].count); - XCTAssertEqual(2, [right getArrangedSubviews].count); - } - }]; -} - -- (void)testPaddingOnSampleCards1 -{ - NSArray *cards = [self prepCards:@[ @"Container.VerticalContentAlignment" ]]; - - ACRRenderResult *renderResult = [ACRRenderer render:cards[0] config:nil widthConstraint:320.0]; - XCTAssertEqual(renderResult.succeeded, YES); - [self dfsAt:renderResult.view - depth:0 - test:^(UIView *testView) { - ACRColumnView *superView = (ACRColumnView *)testView; - ACRColumnView *center = (ACRColumnView *)[superView getArrangedSubviews][2]; - ACRColumnView *bottom = (ACRColumnView *)[superView getArrangedSubviews][4]; - XCTAssertEqual(3, [center getArrangedSubviews].count); - XCTAssertEqual(2, [bottom getArrangedSubviews].count); - }]; -} - -- (void)dfsAt:(UIView *)view depth:(NSUInteger)depth test:(void (^)(UIView *testView))test -{ - if (depth == 0) { - test(view); - } - - if ([view isKindOfClass:[ACRContentStackView class]]) { - for (UIView *subView in [((ACRContentStackView *)view) getArrangedSubviews]) { - [self dfsAt:subView depth:depth - 1 test:test]; + NSArray *testFiles = @[@"Container.VerticalContentAlignment", + @"Container.VerticalContentAlignment2", + @"Column.VerticalAlignment", + @"ColumnSet.FactSet.VerticalStretch", + @"ColumnSet.Image.VerticalStretch", + @"ColumnSet.Input.ChoiceSet.VerticalStretch"]; + NSArray *cards = [self prepCards:testFiles]; + ACRRegistration *registration = [ACRRegistration getInstance]; + ACRCustomContainerRenderer *testContainerRenderer = [ACRCustomContainerRenderer getInstance]; + ACRCustomColumnRenderer *testColumnRenderer = [ACRCustomColumnRenderer getInstance]; + + ACOResourceResolvers *resolvers = [[ACOResourceResolvers alloc] init]; + ADCMockResolver *mockReslover = [[ADCMockResolver alloc] init]; + [resolvers setResourceResolver:mockReslover scheme:@"http"]; + [resolvers setResourceResolver:mockReslover scheme:@"https"]; + ACOHostConfigParseResult *hostconfigParseResult = [ACOHostConfig fromJson:_hostConfig + resourceResolvers:resolvers]; + void (^testBlock) (UIView *, NSDictionary *) = + ^(UIView *view, NSDictionary *dictionary) { + id val = dictionary[@"expected"][@"numberOfViews"]; + if ([val isKindOfClass:[NSString class]]) { + int numberOfPaddings = [((NSString *)val) intValue]; + NSUInteger numberOfViews = [((ACRColumnView *)view) getArrangedSubviews].count; + XCTAssertEqual(numberOfPaddings, numberOfViews); } - } + }; + + testContainerRenderer.fetchActualRenderedView = testBlock; + + testColumnRenderer.fetchActualRenderedView = testBlock; + + [registration setBaseCardElementRenderer:testContainerRenderer cardElementType:ACRContainer useResourceResolver:YES]; + + [registration setBaseCardElementRenderer:testColumnRenderer cardElementType:ACRColumn useResourceResolver:YES]; + + self.continueAfterFailure = NO; + + for (int i = 0; i < cards.count; i++) { + ACOAdaptiveCard *card = cards[i]; + _tearDownMessage = [NSString stringWithFormat:@"The number of padding is not correct when tested with %@", testFiles[i]]; + ACRRenderResult *renderResult = [ACRRenderer render:card config:hostconfigParseResult.config widthConstraint:320.0]; + _tearDownMessage = [NSString stringWithFormat:@"rendering failed for %@ card", testFiles[i]]; + XCTAssertEqual(renderResult.succeeded, YES); + } } @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.h new file mode 100644 index 0000000000..19a35ae83e --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.h @@ -0,0 +1,27 @@ +// +// ACRCustomColumnRenderer.h +// AdaptiveCardsTests +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import "ACRColumnRenderer.h" +#import "ACRContainerRenderer.h" + +@protocol ACRIUnitTest + +@property void (^fetchActualRenderedView) (UIView *view, NSDictionary *props); + +@end + +@interface ACRCustomColumnRenderer : ACRColumnRenderer + ++ (ACRCustomColumnRenderer *)getInstance; + +@end + +@interface ACRCustomContainerRenderer : ACRContainerRenderer + ++ (ACRCustomContainerRenderer *)getInstance;; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.mm new file mode 100644 index 0000000000..27268b8da3 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Helpers/ACRCustomRenderers.mm @@ -0,0 +1,63 @@ +// +// ACRCustomColumnView.m +// AdaptiveCardsTests +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import +#import "ACRCustomRenderers.h" + +@implementation ACRCustomColumnRenderer + ++ (ACRCustomColumnRenderer *)getInstance +{ + static ACRCustomColumnRenderer *singletonInstance = [[self alloc] init]; + return singletonInstance; +} + +- (UIView *)render:(UIView *)viewGroup + rootView:(ACRView *)rootView + inputs:(NSMutableArray *)inputs + baseCardElement:(ACOBaseCardElement *)acoElem + hostConfig:(ACOHostConfig *)acoConfig; +{ + UIView *view = [super render:viewGroup rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:acoConfig]; + if (acoElem.additionalProperty) { + NSError *error = nil; + id props = [NSJSONSerialization JSONObjectWithData:acoElem.additionalProperty options:0 error:&error]; + self.fetchActualRenderedView(view, props); + } + return view; +} + +@synthesize fetchActualRenderedView; + +@end + +@implementation ACRCustomContainerRenderer + ++ (ACRCustomContainerRenderer *)getInstance +{ + static ACRCustomContainerRenderer *singletonInstance = [[self alloc] init]; + return singletonInstance; +} + +- (UIView *)render:(UIView *)viewGroup + rootView:(ACRView *)rootView + inputs:(NSMutableArray *)inputs + baseCardElement:(ACOBaseCardElement *)acoElem + hostConfig:(ACOHostConfig *)acoConfig; +{ + UIView *view = [super render:viewGroup rootView:rootView inputs:inputs baseCardElement:acoElem hostConfig:acoConfig]; + if (acoElem.additionalProperty) { + NSError *error = nil; + id props = [NSJSONSerialization JSONObjectWithData:acoElem.additionalProperty options:0 error:&error]; + self.fetchActualRenderedView(view, props); + } + return view; +} + +@synthesize fetchActualRenderedView; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.h new file mode 100644 index 0000000000..a607b96d4e --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.h @@ -0,0 +1,13 @@ +// +// ADCResolver.h +// ADCResolver.h +// +// Copyright © 2018 Microsoft. All rights reserved. +// + +#import +#import "ACOIResourceResolver.h" + +@interface ADCMockResolver : NSObject + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.m b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.m new file mode 100644 index 0000000000..88f4548f8f --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ADCMockResolver.m @@ -0,0 +1,18 @@ +// +// ADCMockResolver.m +// ADCIOSVisualizer +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import "ADCMockResolver.h" + +@implementation ADCMockResolver + + +- (UIImageView *)resolveImageViewResource:(NSURL *)url +{ + return [[UIImageView alloc] init]; +} + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Column.VerticalAlignment.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Column.VerticalAlignment.json new file mode 100644 index 0000000000..e6cc872e2e --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Column.VerticalAlignment.json @@ -0,0 +1,79 @@ +{ + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "Container", + "items": [ + { + "type": "TextBlock", + "text": "**Columns Vertical Alignment Test**", + "horizontalAlignment": "Center" + } + ], + "style": "emphasis", + "bleed": true + }, + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": "stretch", + "items": [ + { + "type": "Image", + "altText": "", + "url": "https://adaptivecards.io/content/adaptive-card-50.png", + "horizontalAlignment": "Center" + } + ] + }, + { + "type": "Column", + "width": "stretch", + "expected": { + "numberOfViews" : "2" + }, + "items": [ + { + "type": "TextBlock", + "text": "Top", + "horizontalAlignment": "Center", + "wrap": true + } + ] + }, + { + "type": "Column", + "expected": { + "numberOfViews" : "3" + }, + "width": "stretch", + "items": [ + { + "type": "TextBlock", + "text": "Center" + } + ], + "verticalContentAlignment": "Center" + }, + { + "type": "Column", + "expected": { + "numberOfViews" : "2" + }, + "width": "stretch", + "items": [ + { + "type": "TextBlock", + "text": "Bottom" + } + ], + "verticalContentAlignment": "Bottom" + } + ] + } + ], + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json" +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.FactSet.VerticalStretch.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.FactSet.VerticalStretch.json new file mode 100644 index 0000000000..46333bc443 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.FactSet.VerticalStretch.json @@ -0,0 +1,62 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "ColumnSet", + "height": "stretch", + "columns": [ + { + "type": "Column", + "width": 1, + "items": [ + { + "type": "TextBlock", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": 1, + "spacing": "large", + "separator": true, + "expected": { + "numberOfViews" : "3" + }, + "items": [ + { + "type": "TextBlock", + "text": "This is a textblock that doesn't stretch, but the input text does", + "spacing": "none", + "wrap": true + }, + { + "type": "FactSet", + "spacing": "none", + "height": "stretch", + "facts": [ + { + "title": "Fact 1", + "value": "Value 1" + }, + { + "title": "Fact 2", + "value": "Value 2" + } + ] + }, + { + "type": "TextBlock", + "spacing": "none", + "text": "One last text block at the bottom", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Image.VerticalStretch.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Image.VerticalStretch.json new file mode 100644 index 0000000000..d2d5444f40 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Image.VerticalStretch.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "ColumnSet", + "height": "stretch", + "columns": [ + { + "type": "Column", + "width": 1, + "items": [ + { + "type": "TextBlock", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": 1, + "spacing": "large", + "expected": { + "numberOfViews" : "4" + }, + "separator": true, + "items": [ + { + "type": "TextBlock", + "spacing": "none", + "text": "This is a textblock that doesn't stretch, but the input text does", + "wrap": true + }, + { + "type": "Image", + "spacing": "none", + "url": "https://adaptivecards.io/content/cats/1.png", + "height": "stretch", + "size": "small" + }, + { + "type": "TextBlock", + "spacing": "none", + "text": "One last text block at the bottom", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Input.ChoiceSet.VerticalStretch.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Input.ChoiceSet.VerticalStretch.json new file mode 100644 index 0000000000..5a11a1eb33 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.Input.ChoiceSet.VerticalStretch.json @@ -0,0 +1,89 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "ColumnSet", + "height": "stretch", + "columns": [ + { + "type": "Column", + "width": 1, + "items": [ + { + "type": "TextBlock", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": 1, + "spacing": "large", + "separator": true, + "expected": { + "numberOfViews" : "4" + }, + "items": [ + { + "type": "TextBlock", + "text": "This is a textblock that doesn't stretch, but the input text does", + "wrap": true + }, + { + "type": "Input.ChoiceSet", + "id": "input1", + "spacing":"none", + "height": "stretch", + "choices": [ + { + "title": "Red", + "value": "1" + }, + { + "title": "Green", + "value": "2" + }, + { + "title": "Blue", + "value": "3" + } + ] + }, + { + "type": "Input.ChoiceSet", + "id": "myColor2", + "style": "expanded", + "isMultiSelect": false, + "value": "1", + "spacing":"none", + "height": "stretch", + "choices": [ + { + "title": "Red", + "value": "1" + }, + { + "title": "Green", + "value": "2" + }, + { + "title": "Blue", + "value": "3" + } + ] + }, + { + "type": "TextBlock", + "spacing":"none", + "text": "One last text block at the bottom", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.VerticalStretch.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.VerticalStretch.json new file mode 100644 index 0000000000..fcdbcf7cd4 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/ColumnSet.VerticalStretch.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.0", + "body": [ + { + "type": "ColumnSet", + "columns": [ + { + "type": "Column", + "width": 1, + "items": [ + { + "type": "TextBlock", + "text": "The left column has a very, very long text which makes it very tall. Because it is tall, the right column is also very tall. The right column contains two TextBlocks; one is displaying at the top of the column, while the other one is displayed at the bottom. That is achieved by setting the first TextBlock's **height** property to **stretch**.", + "wrap": true + } + ] + }, + { + "type": "Column", + "width": 1, + "spacing": "large", + "separator": true, + "items": [ + { + "type": "TextBlock", + "height": "stretch", + "text": "First TextBlock displayed at the top", + "wrap": true + }, + { + "type": "TextBlock", + "text": "Second TextBlock displayed at the bottom", + "wrap": true + } + ] + } + ] + } + ] +} diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment2.json b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment2.json new file mode 100644 index 0000000000..a48f222432 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/TestFiles/Container.VerticalContentAlignment2.json @@ -0,0 +1,42 @@ +{ + "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", + "type": "AdaptiveCard", + "version": "1.1", + "minHeight": "150px", + "body": [ + { + "type": "TextBlock", + "text": "I'm a regular TextBlock..." + }, + { + "type": "Container", + "height": "stretch", + "spacing": "none", + "verticalContentAlignment": "center", + "expected": { + "numberOfViews" : "3" + }, + "items": [ + { + "type": "TextBlock", + "text": "But I'm centered..." + } + ] + }, + { + "type": "Container", + "height": "stretch", + "spacing": "none", + "verticalContentAlignment": "center", + "expected": { + "numberOfViews" : "3" + }, + "items": [ + { + "type": "TextBlock", + "text": "And I'm like a footer!" + } + ] + } + ] +} From 6aa07d778750666cbe21d069e291f6b45ca35ab7 Mon Sep 17 00:00:00 2001 From: nesalang Date: Tue, 7 Sep 2021 11:25:39 -0700 Subject: [PATCH 08/14] refactored the padding configuration in FactSet to Padding Handler --- .../AdaptiveCards.xcodeproj/project.pbxproj | 18 +++++++++++++----- .../AdaptiveCards/ACOPaddingHandler.h | 2 ++ .../AdaptiveCards/ACOPaddingHandler.mm | 6 +++--- .../AdaptiveCards/ACRFactSetRenderer.mm | 7 ++++--- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 130a71ed3f..eff56e09ed 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -1080,6 +1080,17 @@ path = TestFiles; sourceTree = ""; }; + 6BE6C7C626E7E2A6009E9171 /* ImageSet */ = { + isa = PBXGroup; + children = ( + F401A8791F0DCBC8006D7AF2 /* ACRImageSetRenderer.h */, + F401A87A1F0DCBC8006D7AF2 /* ACRImageSetRenderer.mm */, + F401A8751F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h */, + F401A8761F0DB69B006D7AF2 /* ACRImageSetUICollectionView.mm */, + ); + name = ImageSet; + sourceTree = ""; + }; 6BFCA139264F548600195CA7 /* TableView */ = { isa = PBXGroup; children = ( @@ -1413,7 +1424,6 @@ F42979341F3007C500E89914 /* ReadOnlyObjects */ = { isa = PBXGroup; children = ( - F42741061EF8624F00399FBB /* ACRIBaseCardElementRenderer.h */, F42741081EF864A900399FBB /* ACRBaseCardElementRenderer.h */, F42741091EF864A900399FBB /* ACRBaseCardElementRenderer.mm */, F42741261EFB374A00399FBB /* ACRColumnSetRenderer.h */, @@ -1424,14 +1434,11 @@ F4F44B9E204CED2300A2F24C /* ACRCustomRenderer.mm */, F43A940E1F1D60E30001920B /* ACRFactSetRenderer.h */, F43A940F1F1D60E30001920B /* ACRFactSetRenderer.mm */, + F42741061EF8624F00399FBB /* ACRIBaseCardElementRenderer.h */, 6B74403725BA71B60051F2A1 /* ACRImageProperties.h */, 6B74403825BA71B60051F2A1 /* ACRImageProperties.mm */, F42741101EF873A600399FBB /* ACRImageRenderer.h */, F42741111EF873A600399FBB /* ACRImageRenderer.mm */, - F401A8791F0DCBC8006D7AF2 /* ACRImageSetRenderer.h */, - F401A87A1F0DCBC8006D7AF2 /* ACRImageSetRenderer.mm */, - F401A8751F0DB69B006D7AF2 /* ACRImageSetUICollectionView.h */, - F401A8761F0DB69B006D7AF2 /* ACRImageSetUICollectionView.mm */, 6B096D4C225431D0006CC034 /* ACRRichTextBlockRenderer.h */, 6B096D4D225431D0006CC034 /* ACRRichTextBlockRenderer.mm */, F42741141EF895AB00399FBB /* ACRTextBlockRenderer.h */, @@ -1441,6 +1448,7 @@ F4F44B6A203FA8EF00A2F24C /* ACRUILabel.h */, F4F44B6D203FAF9300A2F24C /* ACRUILabel.mm */, 6B124C9626B9F519007E9641 /* Columns */, + 6BE6C7C626E7E2A6009E9171 /* ImageSet */, 6BB2120621000024009EA1BA /* Media */, ); name = ReadOnlyObjects; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h index e029a58b03..104c1770d5 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h @@ -22,4 +22,6 @@ - (BOOL)isPadding:(UIView *)padding; ++ (void)configureHugging:(UIView *)view; + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm index 3348b79494..7f6b1583b6 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm @@ -40,7 +40,7 @@ - (UIView *)addPaddingFor:(UIView *)view UIView *padding = [[UIView alloc] init]; - [self configureHugging:padding]; + [ACOPaddingHandler configureHugging:padding]; NSMutableArray *values = [_paddingMap objectForKey:view]; if (!values) { @@ -54,7 +54,7 @@ - (UIView *)addPaddingFor:(UIView *)view return padding; } -- (void)configureHugging:(UIView *)view ++ (void)configureHugging:(UIView *)view { view.translatesAutoresizingMaskIntoConstraints = NO; [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; @@ -68,7 +68,7 @@ - (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement if ((HeightType::Stretch == correspondingElement.element->GetHeight()) && (correspondingElement.type != ACRImage)) { - [self configureHugging:view]; + [ACOPaddingHandler configureHugging:view]; [_stretchableViewSet addObject:view]; } } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm index 1e94589415..e26940e4c9 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRFactSetRenderer.mm @@ -7,6 +7,7 @@ #import "ACRFactSetRenderer.h" #import "ACOBaseCardElementPrivate.h" #import "ACOHostConfigPrivate.h" +#import "ACOPaddingHandler.h" #import "ACRColumnSetView.h" #import "ACRContentHoldingUIView.h" #import "ACRRegistration.h" @@ -193,11 +194,11 @@ - (UIView *)render:(UIView *)viewGroup if (elem->GetHeight() == HeightType::Stretch) { if (titleStack.arrangedSubviews.count) { - [titleStack.arrangedSubviews.lastObject setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + [ACOPaddingHandler configureHugging:titleStack.arrangedSubviews.lastObject]; } - + if (valueStack.arrangedSubviews.count) { - [valueStack.arrangedSubviews.lastObject setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; + [ACOPaddingHandler configureHugging:valueStack.arrangedSubviews.lastObject]; } } From 82da0aecdcd6a52b1c25d0d6c1519e425b70d976 Mon Sep 17 00:00:00 2001 From: nesalang Date: Wed, 15 Sep 2021 22:16:31 -0700 Subject: [PATCH 09/14] [iOS] Updated VerticalContentAlignment and Height --- .../AdaptiveCards.xcodeproj/project.pbxproj | 32 +- .../AdaptiveCards/ACOFillerSpaceManager.h | 48 +++ .../AdaptiveCards/ACOFillerSpaceManager.mm | 171 ++++++++++ .../AdaptiveCards/ACOPaddingHandler.h | 27 -- .../AdaptiveCards/ACOPaddingHandler.mm | 103 ------ .../AdaptiveCards/ACOVisibilityManager.h | 27 +- .../AdaptiveCards/ACOVisibilityManager.mm | 267 ++++++++++------ .../AdaptiveCards/ACRColumnRenderer.mm | 14 +- .../AdaptiveCards/ACRColumnSetRenderer.mm | 34 +- .../AdaptiveCards/ACRColumnSetView.mm | 21 ++ .../AdaptiveCards/ACRColumnView.h | 19 +- .../AdaptiveCards/ACRColumnView.mm | 77 +---- .../AdaptiveCards/ACRContainerRenderer.mm | 13 +- .../AdaptiveCards/ACRContentHoldingUIView.mm | 1 + .../AdaptiveCards/ACRContentStackView.h | 34 +- .../AdaptiveCards/ACRContentStackView.mm | 183 +++++++++-- .../AdaptiveCards/ACRFactSetRenderer.mm | 8 +- .../AdaptiveCards/ACRIContentHoldingView.h | 16 +- .../AdaptiveCards/ACRImageRenderer.mm | 4 +- .../AdaptiveCards/ACRImageSetRenderer.mm | 2 - .../ACRInputChoiceSetRenderer.mm | 2 - .../AdaptiveCards/ACRInputDateRenderer.mm | 4 +- .../AdaptiveCards/ACRInputNumberRenderer.mm | 6 +- .../AdaptiveCards/ACRInputRenderer.mm | 2 - .../AdaptiveCards/ACRInputTimeRenderer.mm | 6 +- .../AdaptiveCards/ACRInputToggleRenderer.mm | 4 +- .../AdaptiveCards/ACRMediaRenderer.mm | 6 +- .../AdaptiveCards/ACRRenderer.mm | 16 +- .../AdaptiveCards/ACRRichTextBlockRenderer.mm | 2 - .../AdaptiveCards/ACRTextBlockRenderer.mm | 2 - .../ACRToggleVisibilityTarget.mm | 29 +- .../AdaptiveCardsActionsTest.mm | 3 +- .../AdaptiveCardsColumnTests.mm | 301 +++++++++--------- .../AdaptiveCardsTests/Mocks/ACRMockViews.h | 26 ++ .../Mocks/{MockACRView.mm => ACRMockViews.mm} | 24 +- .../AdaptiveCardsTests/Mocks/MockACRView.h | 16 - 36 files changed, 894 insertions(+), 656 deletions(-) create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.h create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.mm delete mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h delete mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm create mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/ACRMockViews.h rename source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/{MockACRView.mm => ACRMockViews.mm} (54%) delete mode 100644 source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCardsTests/Mocks/MockACRView.h diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj index 85febbd7ff..80bd3736c8 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards.xcodeproj/project.pbxproj @@ -40,7 +40,7 @@ 6B1147D91F32F922008846EC /* ACRShowCardTarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B1147D71F32F922008846EC /* ACRShowCardTarget.mm */; }; 6B124C8C26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C8B26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm */; }; 6B124C8E26B4B3CD007E9641 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B124C8D26B4B3CD007E9641 /* UIKit.framework */; }; - 6B124C9026B8AC37007E9641 /* MockACRView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C8F26B8AC37007E9641 /* MockACRView.mm */; }; + 6B124C9026B8AC37007E9641 /* ACRMockViews.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C8F26B8AC37007E9641 /* ACRMockViews.mm */; }; 6B124C9526B8AE72007E9641 /* MockContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9426B8AE72007E9641 /* MockContext.mm */; }; 6B124C9826B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */; }; 6B124C9B26B9F7AD007E9641 /* ACOVisibilityManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B124C9A26B9F7AD007E9641 /* ACOVisibilityManager.mm */; }; @@ -176,9 +176,10 @@ 6BCE4B292108FBD800021A62 /* ACRLongPressGestureRecognizerFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BCE4B282108FBD800021A62 /* ACRLongPressGestureRecognizerFactory.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BD025ED254784670009B019 /* ACOInputResults.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD025EB254784660009B019 /* ACOInputResults.h */; settings = {ATTRIBUTES = (Public, ); }; }; 6BD025EE254784670009B019 /* ACOInputResults.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD025EC254784670009B019 /* ACOInputResults.mm */; }; + 6BD859FB26F2CA7B0086F5BA /* ACOFillerSpaceManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD859F826F2CA7B0086F5BA /* ACOFillerSpaceManager.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6BD859FC26F2CA7B0086F5BA /* ACOFillerSpaceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD859F926F2CA7B0086F5BA /* ACOFillerSpaceManager.mm */; }; + 6BD859FF26F2E1920086F5BA /* Action.ToggleVisibilityExhaustive.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BD859FE26F2E1920086F5BA /* Action.ToggleVisibilityExhaustive.json */; }; 6BD90D8E24C37D5900B040FB /* ACRInputLabelViewPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BD90D8D24C37D5900B040FB /* ACRInputLabelViewPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 6BDD085526D5727C00969B41 /* ACOPaddingHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */; }; 6BE6C7B026E2C9A3009E9171 /* ACRCustomRenderers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BE6C7AF26E2C9A3009E9171 /* ACRCustomRenderers.mm */; }; 6BE6C7B226E2DF29009E9171 /* Container.VerticalContentAlignment2.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B126E2DF29009E9171 /* Container.VerticalContentAlignment2.json */; }; 6BE6C7B626E2E140009E9171 /* Column.VerticalAlignment.json in Resources */ = {isa = PBXBuildFile; fileRef = 6BE6C7B526E2E140009E9171 /* Column.VerticalAlignment.json */; }; @@ -477,8 +478,8 @@ 6B1147D71F32F922008846EC /* ACRShowCardTarget.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRShowCardTarget.mm; sourceTree = ""; }; 6B124C8B26B4AA07007E9641 /* AdaptiveCardsActionsTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdaptiveCardsActionsTest.mm; sourceTree = ""; }; 6B124C8D26B4B3CD007E9641 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; - 6B124C8F26B8AC37007E9641 /* MockACRView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockACRView.mm; sourceTree = ""; }; - 6B124C9126B8AC58007E9641 /* MockACRView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockACRView.h; sourceTree = ""; }; + 6B124C8F26B8AC37007E9641 /* ACRMockViews.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRMockViews.mm; sourceTree = ""; }; + 6B124C9126B8AC58007E9641 /* ACRMockViews.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACRMockViews.h; sourceTree = ""; }; 6B124C9326B8AE59007E9641 /* MockContext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockContext.h; sourceTree = ""; }; 6B124C9426B8AE72007E9641 /* MockContext.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MockContext.mm; sourceTree = ""; }; 6B124C9726B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AdaptiveCardsColumnTests.mm; sourceTree = ""; }; @@ -613,9 +614,10 @@ 6BCE4B282108FBD800021A62 /* ACRLongPressGestureRecognizerFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRLongPressGestureRecognizerFactory.h; sourceTree = ""; }; 6BD025EB254784660009B019 /* ACOInputResults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOInputResults.h; sourceTree = ""; }; 6BD025EC254784670009B019 /* ACOInputResults.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOInputResults.mm; sourceTree = ""; }; + 6BD859F826F2CA7B0086F5BA /* ACOFillerSpaceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOFillerSpaceManager.h; sourceTree = ""; }; + 6BD859F926F2CA7B0086F5BA /* ACOFillerSpaceManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOFillerSpaceManager.mm; sourceTree = ""; }; + 6BD859FE26F2E1920086F5BA /* Action.ToggleVisibilityExhaustive.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Action.ToggleVisibilityExhaustive.json; sourceTree = ""; }; 6BD90D8D24C37D5900B040FB /* ACRInputLabelViewPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACRInputLabelViewPrivate.h; sourceTree = ""; }; - 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ACOPaddingHandler.h; sourceTree = ""; }; - 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ACOPaddingHandler.mm; sourceTree = ""; }; 6BE6C7A426E17132009E9171 /* ColumnSet.VerticalStretch.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = ColumnSet.VerticalStretch.json; path = ../../../../../../samples/v1.1/Tests/ColumnSet.VerticalStretch.json; sourceTree = ""; }; 6BE6C7AE26E2C969009E9171 /* ACRCustomRenderers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ACRCustomRenderers.h; sourceTree = ""; }; 6BE6C7AF26E2C9A3009E9171 /* ACRCustomRenderers.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ACRCustomRenderers.mm; sourceTree = ""; }; @@ -908,8 +910,8 @@ children = ( 6BE6C7C026E2ECEA009E9171 /* ADCMockResolver.h */, 6BE6C7BF26E2ECEA009E9171 /* ADCMockResolver.m */, - 6B124C8F26B8AC37007E9641 /* MockACRView.mm */, - 6B124C9126B8AC58007E9641 /* MockACRView.h */, + 6B124C8F26B8AC37007E9641 /* ACRMockViews.mm */, + 6B124C9126B8AC58007E9641 /* ACRMockViews.h */, 6B124C9326B8AE59007E9641 /* MockContext.h */, 6B124C9426B8AE72007E9641 /* MockContext.mm */, ); @@ -919,8 +921,8 @@ 6B124C9626B9F519007E9641 /* Columns */ = { isa = PBXGroup; children = ( - 6BDD085226D5727C00969B41 /* ACOPaddingHandler.h */, - 6BDD085326D5727C00969B41 /* ACOPaddingHandler.mm */, + 6BD859F826F2CA7B0086F5BA /* ACOFillerSpaceManager.h */, + 6BD859F926F2CA7B0086F5BA /* ACOFillerSpaceManager.mm */, 6B124C9926B9F6B1007E9641 /* ACOVisibilityManager.h */, 6B124C9A26B9F7AD007E9641 /* ACOVisibilityManager.mm */, F42741221EFB274C00399FBB /* ACRColumnRenderer.h */, @@ -1094,6 +1096,7 @@ 6BE6C79F26E17077009E9171 /* TestFiles */ = { isa = PBXGroup; children = ( + 6BD859FE26F2E1920086F5BA /* Action.ToggleVisibilityExhaustive.json */, 6BE6C7B526E2E140009E9171 /* Column.VerticalAlignment.json */, 6BE6C7B926E2E30A009E9171 /* ColumnSet.FactSet.VerticalStretch.json */, 6BE6C7B826E2E30A009E9171 /* ColumnSet.Image.VerticalStretch.json */, @@ -1719,7 +1722,7 @@ 6B124C9F26BCB2FE007E9641 /* ACOVisibilityManager.h in Headers */, 6B92A7E62677DFAB00CAE3BF /* ACRChoiceSetCompactStyleView.h in Headers */, 0D3485FD26193BA500614EB9 /* ACROverflowTarget.h in Headers */, - 6BDD085426D5727C00969B41 /* ACOPaddingHandler.h in Headers */, + 6BD859FB26F2CA7B0086F5BA /* ACOFillerSpaceManager.h in Headers */, 0D45F5A7261731E400EF03C5 /* ACRActionOverflowRenderer.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1849,6 +1852,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 6BD859FF26F2E1920086F5BA /* Action.ToggleVisibilityExhaustive.json in Resources */, 6BE6C7C526E7CDD0009E9171 /* ColumnSet.Input.ChoiceSet.VerticalStretch.json in Resources */, 6BE6C7BA26E2E30A009E9171 /* Container.VerticalContentAlignment.json in Resources */, 6BE6C7BE26E2E4EC009E9171 /* ColumnSet.VerticalStretch.json in Resources */, @@ -1910,9 +1914,9 @@ F44873171EE2261F00FCAFAE /* jsoncpp.cpp in Sources */, F42E51781FEC3840008F9642 /* MarkDownHtmlGenerator.cpp in Sources */, F49683551F6CA24600DF0D3A /* ACRRenderResult.mm in Sources */, - 6BDD085526D5727C00969B41 /* ACOPaddingHandler.mm in Sources */, 6BFF9A0526004C580028069F /* ACOAuthentication.mm in Sources */, 6B8C765226449B09009548FA /* TableColumnDefinition.cpp in Sources */, + 6BD859FC26F2CA7B0086F5BA /* ACOFillerSpaceManager.mm in Sources */, 6BBE841A23CD184D00ECA586 /* ACOWarning.mm in Sources */, 0D34860226193C7900614EB9 /* ACROverflowTarget.mm in Sources */, F44873071EE2261F00FCAFAE /* DateInput.cpp in Sources */, @@ -2060,7 +2064,7 @@ 30D56DEF2682AB9C00D6E418 /* AdaptiveCardsTextBlockTests.mm in Sources */, 6B124C9526B8AE72007E9641 /* MockContext.mm in Sources */, 30D56DE9268298B300D6E418 /* AdaptiveCardsTests.mm in Sources */, - 6B124C9026B8AC37007E9641 /* MockACRView.mm in Sources */, + 6B124C9026B8AC37007E9641 /* ACRMockViews.mm in Sources */, 6B124C9826B9F5FC007E9641 /* AdaptiveCardsColumnTests.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.h new file mode 100644 index 0000000000..b433acd9a7 --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.h @@ -0,0 +1,48 @@ +// +// ACOPaddingHandler.h +// AdaptiveCards +// +// Copyright © 2021 Microsoft. All rights reserved. +// + +#import +#import + +@class ACOBaseCardElement; +@class ACRSeparator; + +extern const CGFloat kFillerViewLayoutConstraintPriority; + +@interface ACOFillerSpaceManager : NSObject + +/// tells if the owner of this object has padding +@property (nonatomic) BOOL hasPadding; + +/// configures & adds padding for the `view` +/// having padding makes the owner of the object, +/// stretchable +- (UIView *)addPaddingFor:(UIView *)view; + +/// configures for AdaptiveCards Height property +/// Image and Media gets their own padding since stretching them are not desirable +- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; + +/// activates the constraints together for performance +/// two stretchable views get same height +/// by setting low priority, the relationship can be overridden +/// if it's not possible +- (NSArray *)activateConstraintsForPadding; + +- (void)deActivateConstraintsForPadding; + +- (BOOL)isPadding:(UIView *)padding; + +- (NSArray *)getFillerSpaceView:(UIView *)view; + +- (void)associateSeparatorWithOwnerVew:separator ownerView:ownerView; + +- (ACRSeparator *)getSeparatorForOwnerView:(UIView *)ownerView; + ++ (void)configureHugging:(UIView *)view; + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.mm new file mode 100644 index 0000000000..46019607ad --- /dev/null +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOFillerSpaceManager.mm @@ -0,0 +1,171 @@ +// +// ACOPaddingHandler.mm +// AdaptiveCards +// +// Copyright © 2021 Microsoft. All rights reserved. +// +// + +#import "ACOFillerSpaceManager.h" +#import "ACOBaseCardElementPrivate.h" + +const CGFloat kFillerViewLayoutConstraintPriority = UILayoutPriorityDefaultLow - 10; + +@implementation ACOFillerSpaceManager { + NSMapTable *> *_paddingMap; + NSMapTable *_separatorMap; + NSHashTable *_stretchableViewSet; + NSMutableArray *_stretchableViews; + NSHashTable *_paddingSet; + NSMutableArray *_paddingConstraints; +} + +- (instancetype)init +{ + self = [super init]; + if (self) { + _paddingMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory]; + _separatorMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory]; + _stretchableViewSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; + _stretchableViews = [[NSMutableArray alloc] init]; + _paddingSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; + } + return self; +} + +/// tells if the owner of this object has padding +- (BOOL)hasPadding +{ + if (_stretchableViews) { + if (_stretchableViews.count) { + for (NSValue *value in _stretchableViews) { + UIView *view = value.nonretainedObjectValue; + if (!view.isHidden) { + return YES; + } + } + } + } + return NO; +} + +/// configures & adds padding for the `view` +/// having padding makes the owner of the object, +/// stretchable +- (UIView *)addPaddingFor:(UIView *)view +{ + if (!view) { + return nil; + } + + UIView *padding = [[UIView alloc] init]; + + [ACOFillerSpaceManager configureHugging:padding]; + + NSMutableArray *values = [_paddingMap objectForKey:view]; + if (!values) { + values = [[NSMutableArray alloc] init]; + [_paddingMap setObject:values forKey:view]; + } + + [_stretchableViewSet addObject:padding]; + [_stretchableViews addObject:[NSValue valueWithNonretainedObject:padding]]; + [_paddingSet addObject:padding]; + [values addObject:[NSValue valueWithNonretainedObject:padding]]; + + return padding; +} + ++ (void)configureHugging:(UIView *)view +{ + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setContentHuggingPriority:kFillerViewLayoutConstraintPriority forAxis:UILayoutConstraintAxisVertical]; +} + +/// configures for AdaptiveCards Height property +/// Image and Media gets their own padding since stretching them are not desirable +- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement +{ + if (!view || !correspondingElement || !correspondingElement.element) { + return; + } + + if ((HeightType::Stretch == correspondingElement.element->GetHeight()) && + (correspondingElement.type != ACRImage && + correspondingElement.type != ACRMedia)) { + [ACOFillerSpaceManager configureHugging:view]; + [_stretchableViewSet addObject:view]; + [_stretchableViews addObject:[NSValue valueWithNonretainedObject:view]]; + } +} + +/// activates the constraints together for performance +/// two stretchable views get same height +/// by setting low priority, the relationship can be overridden +/// if it's not possible +- (NSArray *)activateConstraintsForPadding +{ + if (_stretchableViews.count > 1) { + NSMutableArray *_paddingConstraints = [[NSMutableArray alloc] init]; + UIView *prevPadding = nil; + for (NSValue *paddingValue in _stretchableViews) { + UIView *padding = paddingValue.nonretainedObjectValue; + if (prevPadding && !padding.isHidden) { + [_paddingConstraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; + _paddingConstraints.lastObject.priority = UILayoutPriorityDefaultLow; + } + if (!padding.isHidden) { + prevPadding = padding; + } + } + + if (_paddingConstraints && _paddingConstraints.count) { + [NSLayoutConstraint activateConstraints:_paddingConstraints]; + } + + return _paddingConstraints; + } + return nil; +} + +- (void)deActivateConstraintsForPadding +{ + if (_paddingConstraints && _paddingConstraints.count) { + [NSLayoutConstraint deactivateConstraints:_paddingConstraints]; + } +} + +- (BOOL)isPadding:(UIView *)padding +{ + if (!padding) { + return NO; + } + return [_paddingSet containsObject:padding]; +} + +- (NSArray *)getFillerSpaceView:(UIView *)view +{ + if (_paddingMap) { + return [_paddingMap objectForKey:view]; + } + return nil; +} + +- (void)associateSeparatorWithOwnerVew:separator ownerView:ownerView +{ + [_separatorMap setObject:[NSValue valueWithNonretainedObject:separator] forKey:ownerView]; +} + +- (ACRSeparator *)getSeparatorForOwnerView:(UIView *)ownerView +{ + if (!ownerView) { + return nil; + } + NSValue *value = [_separatorMap objectForKey:ownerView]; + if (value) { + return value.nonretainedObjectValue; + } + return nil; +} + +@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h deleted file mode 100644 index 104c1770d5..0000000000 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// ACOPaddingHandler.h -// AdaptiveCards -// -// Copyright © 2021 Microsoft. All rights reserved. -// - -#import -#import - -@class ACOBaseCardElement; - -@interface ACOPaddingHandler : NSObject - -@property (nonatomic) BOOL hasPadding; - -- (UIView *)addPaddingFor:(UIView *)view; - -- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement; - -- (NSArray *)activateConstraintsForPadding; - -- (BOOL)isPadding:(UIView *)padding; - -+ (void)configureHugging:(UIView *)view; - -@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm deleted file mode 100644 index 7f6b1583b6..0000000000 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOPaddingHandler.mm +++ /dev/null @@ -1,103 +0,0 @@ -// -// ACOPaddingHandler.mm -// AdaptiveCards -// -// Copyright © 2021 Microsoft. All rights reserved. -// -// - -#import "ACOPaddingHandler.h" -#import "ACOBaseCardElementPrivate.h" - -@implementation ACOPaddingHandler { - NSMapTable *> *_paddingMap; - NSHashTable *_stretchableViewSet; -} - -- (instancetype)init -{ - self = [super init]; - if (self) { - _paddingMap = [NSMapTable mapTableWithKeyOptions:NSMapTableWeakMemory valueOptions:NSMapTableStrongMemory]; - _stretchableViewSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:5]; - } - return self; -} - -- (BOOL)hasPadding -{ - if (_stretchableViewSet) { - return _stretchableViewSet.count != 0; - } - return NO; -} - -- (UIView *)addPaddingFor:(UIView *)view -{ - if (!view) { - return nil; - } - - UIView *padding = [[UIView alloc] init]; - - [ACOPaddingHandler configureHugging:padding]; - - NSMutableArray *values = [_paddingMap objectForKey:view]; - if (!values) { - values = [[NSMutableArray alloc] init]; - [_paddingMap setObject:values forKey:view]; - } - - [_stretchableViewSet addObject:padding]; - [values addObject:[NSValue valueWithNonretainedObject:padding]]; - - return padding; -} - -+ (void)configureHugging:(UIView *)view -{ - view.translatesAutoresizingMaskIntoConstraints = NO; - [view setContentHuggingPriority:UILayoutPriorityDefaultLow - 10 forAxis:UILayoutConstraintAxisVertical]; -} - -- (void)configureHeight:(UIView *)view correspondingElement:(ACOBaseCardElement *)correspondingElement -{ - if (!view || !correspondingElement || !correspondingElement.element) { - return; - } - - if ((HeightType::Stretch == correspondingElement.element->GetHeight()) && - (correspondingElement.type != ACRImage)) { - [ACOPaddingHandler configureHugging:view]; - [_stretchableViewSet addObject:view]; - } -} - -- (NSArray *)activateConstraintsForPadding -{ - if (_stretchableViewSet.count > 1) { - NSMutableArray *constraints = [[NSMutableArray alloc] init]; - UIView *prevPadding = nil; - for (UIView *padding in _stretchableViewSet.objectEnumerator) { - if (prevPadding) { - [constraints addObject:[prevPadding.heightAnchor constraintEqualToAnchor:padding.heightAnchor]]; - constraints.lastObject.priority = UILayoutPriorityDefaultLow; - } - prevPadding = padding; - } - - if (constraints && constraints.count) { - [NSLayoutConstraint activateConstraints:constraints]; - } - - return constraints; - } - return nil; -} - -- (BOOL)isPadding:(UIView *)padding -{ - return [_stretchableViewSet containsObject:padding]; -} - -@end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h index 64002cc712..4d07c4b4e5 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.h @@ -5,27 +5,40 @@ // Copyright © 2021 Microsoft. All rights reserved. // -#import +#import "ACOFillerSpaceManager.h" #import +#import @protocol ACOIVisibilityManagerFacade - (void)hideView:(UIView *)view; - (void)unhideView:(UIView *)view; +- (void)updatePaddingVisibility; @end +@class ACRContentStackView; + @interface ACOVisibilityManager : NSObject -@property (copy) NSString *columnWidth; +@property (nonatomic) BOOL hasVisibleViews; + +- (instancetype)init:(ACOFillerSpaceManager *)fillerSpaceManager; + +/// hide `viewToBeHidden`. `hostView` is a superview of type ColumnView or ColumnSetView +- (void)hideView:(UIView *)viewToBeHidden hostView:(ACRContentStackView *)hostView; -/// hides viewToBeHidden from arrangedSubViews -- (void)hideView:(UIView *)viewToBeHidden arrangedSubviews:(NSArray *)subviews; +/// unhide `viewToBeUnhidden`. `hostView` is a superview of type ColumnView or ColumnSetView +- (void)unhideView:(UIView *)viewToBeUnhidden hostView:(ACRContentStackView *)hostView; -- (void)unhideView:(UIView *)viewToBeUnhidden arrangedSubviews:(NSArray *)subviews; +/// change the visibility of the padding of a host view to `visibility` +/// `visibility` `YES` indicates that the padding will be hidden +- (void)changeVisibilityOfPadding:(UIView *)hostView visibilityValue:(BOOL)visibility; -- (void)addPadding:(UIView *)padding; +- (void)updatePaddingVisibility; -- (BOOL)isPadding:(UIView *)padding; +/// adds index of a visible view to a visible views collection, and the index is maintained in sorted order +/// in increasing order. +- (void)addVisibleView:(NSUInteger)index; @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm index a4d6683d86..df4b0755df 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACOVisibilityManager.mm @@ -7,151 +7,212 @@ // #import "ACOVisibilityManager.h" +#import "ACRColumnView.h" #import "ACREnums.h" #import "ACRSeparator.h" @implementation ACOVisibilityManager { - NSHashTable *_paddingSet; + /// tracks objects that are used in filling the space + __weak ACOFillerSpaceManager *_fillerSpaceManager; + /// tracks visible views + NSMutableOrderedSet *_visibleViews; } -- (instancetype)init { +- (instancetype)init:(ACOFillerSpaceManager *)fillerSpaceManager +{ self = [super init]; if (self) { - _paddingSet = [[NSHashTable alloc] initWithOptions:NSHashTableWeakMemory capacity:2]; + _fillerSpaceManager = fillerSpaceManager; + _visibleViews = [[NSMutableOrderedSet alloc] init]; } return self; } -- (void)hideView:(UIView *)viewToBeHidden arrangedSubviews:(NSArray *)subviews +- (BOOL)hasVisibleViews +{ + return _visibleViews.count > 0; +} + +/// adds index of a visible view to a visible views collection, and the index is maintained in sorted order +/// in increasing order. +- (void)addVisibleView:(NSUInteger)index { - if (!subviews || !viewToBeHidden) { + NSNumber *indexAsNumber = [NSNumber numberWithLong:index]; + if ([_visibleViews containsObject:indexAsNumber]) { return; } + NSRange range = NSMakeRange(0, _visibleViews.count); + NSUInteger insertionIndex = [_visibleViews indexOfObject:indexAsNumber + inSortedRange:range + options:NSBinarySearchingInsertionIndex + usingComparator:^(id num0, id num1) { + return [num0 compare:num1]; + }]; + [_visibleViews insertObject:indexAsNumber atIndex:insertionIndex]; +} - if (!viewToBeHidden.isHidden) { - viewToBeHidden.hidden = YES; +/// removes the index of view from the visible views collection +/// maintain the sorted order after the removal +- (void)removeVisibleViewAtIndex:(NSUInteger)index +{ + NSNumber *indexAsNumber = [NSNumber numberWithLong:index]; + if (![_visibleViews containsObject:indexAsNumber]) { + return; } + NSRange range = NSMakeRange(0, _visibleViews.count); + NSUInteger removalIndex = [_visibleViews indexOfObject:indexAsNumber + inSortedRange:range + options:NSBinarySearchingInsertionIndex + usingComparator:^(id num0, id num1) { + return [num0 compare:num1]; + }]; + [_visibleViews removeObjectAtIndex:removalIndex]; +} - // get the index of the target view - // if the target view is not the tail, - // check to the right, and if the right separator and not hidden, - // hide the separator - // if the target view is tail, check to left, and if the left is separator and not hidden, - // hide the separator - NSInteger index = [subviews indexOfObject:viewToBeHidden]; - if (index + 1 < subviews.count) { - NSInteger nextViewIndex = index + 1; - UIView *nextView = subviews[nextViewIndex]; - if ([nextView isKindOfClass:[ACRSeparator class]]) { - if (!nextView.isHidden) { - nextView.hidden = YES; - } - } - } else if (index - 1 >= 0) { - NSInteger prevViewIndex = index - 1; - UIView *prevView = subviews[prevViewIndex]; - if ([prevView isKindOfClass:[ACRSeparator class]]) { - if (!prevView.isHidden) { - prevView.hidden = YES; - } +/// YES means the index of view is currently the leading or top view +- (BOOL)amIHead:(NSUInteger)index +{ + NSNumber *indexAsNumber = [NSNumber numberWithLong:index]; + return (_visibleViews.count && [_visibleViews.firstObject isEqual:indexAsNumber]); +} + +/// returns the current leading or top view's index +- (NSUInteger)getHeadIndexOfVisibleViews +{ + if (_visibleViews.count) { + return [_visibleViews.firstObject longValue]; + } + + return NSNotFound; +} + +/// change the visibility of the separator of a host view to `visibility` +/// `visibility` `YES` indicates that the separator will be hidden +- (void)changeVisiblityOfSeparator:(UIView *)hostView visibilityValue:(BOOL)visibility contentStackView:(ACRContentStackView *)contentStackView +{ + ACRSeparator *separtor = [_fillerSpaceManager getSeparatorForOwnerView:hostView]; + if (separtor && (separtor.isHidden != visibility)) { + separtor.hidden = visibility; + if (visibility) { + [contentStackView decreaseIntrinsicContentSize:separtor]; + } else { + [contentStackView increaseIntrinsicContentSize:separtor]; } } +} - // only cares for tailing padding at the moment - // if column width is "auto", and all of its children hidden, - // hide column, if is "stretch", then don't hide it. - if (_paddingSet.count && [_columnWidth isEqualToString:@"auto"]) { - for (UIView *subview in subviews) { - if (!subview.isHidden && ![self isPadding:subview]) { - return; - } +/// change the visibility of the padding of a host view to `visibility` +/// `visibility` `YES` indicates that the padding will be hidden +- (void)changeVisibilityOfPadding:(UIView *)hostView visibilityValue:(BOOL)visibility +{ + for (NSValue *value in [_fillerSpaceManager getFillerSpaceView:hostView]) { + UIView *padding = value.nonretainedObjectValue; + if (padding.isHidden != visibility) { + padding.hidden = visibility; } + } +} - [self changeVisibilitOfPaddingsTo:YES]; +/// change the visibility of the padding(s) and separator of a host view to `visibility` +/// `visibility` `YES` indicates that the padding will be hidden +- (void)changeVisiblityOfAssociatedViews:(UIView *)hostView + visibilityValue:(BOOL)visibility + contentStackView:(ACRContentStackView *)contentStackView +{ + if (!hostView) { + return; } + + [self changeVisibilityOfPadding:hostView visibilityValue:visibility]; + [self changeVisiblityOfSeparator:hostView visibilityValue:visibility contentStackView:contentStackView]; } -- (void)unhideView:(UIView *)viewToBeUnhidden arrangedSubviews:(NSArray *)subviews +/// hide `viewToBeHidden`. `hostView` is a superview of type ColumnView or ColumnSetView +- (void)hideView:(UIView *)viewToBeHidden hostView:(ACRContentStackView *)hostView { - if (!subviews || !viewToBeUnhidden) { + if (!hostView || !hostView.subviews || !viewToBeHidden || !_fillerSpaceManager) { return; } - // find the index of target view and check if the view is at tail - // if tail, check to left for separator else check to right - // if trailing padding exists, the target view is tail and padding is hidden, - // unhide the padding regardless - BOOL isViewFound = NO, isLastView = YES, isPadding = NO; - NSInteger targetIndex = subviews.count; - NSUInteger visibleViewCounts = 0; - for (NSInteger i = 0; i < subviews.count; i++) { - UIView *subview = subviews[i]; - isPadding = [self isPadding:subview]; - if (!subview.isHidden && !isPadding) { - visibleViewCounts += 1; - } - if ([subview isEqual:viewToBeUnhidden]) { - targetIndex = i; - isViewFound = YES; - if (subview.isHidden) { - visibleViewCounts += 1; - } - } else if (!subview.isHidden && isViewFound && (!_paddingSet.count || (!isPadding))) { - isLastView = NO; - break; - } - } - - if (isViewFound) { - UIView *separator = nil; - NSInteger separatorIndex = targetIndex; - if (isLastView) { - separatorIndex -= 1; - if (separatorIndex > 0 && [subviews[separatorIndex] isKindOfClass:[ACRSeparator class]]) { - separator = subviews[separatorIndex]; - } - } else { - separatorIndex += 1; - if (separatorIndex < subviews.count && [subviews[separatorIndex] isKindOfClass:[ACRSeparator class]]) { - separator = subviews[separatorIndex]; - } - } + NSArray *subviews = [hostView getContentStackSubviews]; + if (!subviews) { + return; + } - UIView *targetView = subviews[targetIndex]; - if (targetView.isHidden) { - targetView.hidden = NO; - } - - if (visibleViewCounts > 1 && separator && separator.isHidden) { - separator.hidden = NO; - } + NSUInteger index = [subviews indexOfObject:viewToBeHidden]; + if (index == NSNotFound) { + return; + } + + BOOL isHead = [self amIHead:index]; + [self removeVisibleViewAtIndex:index]; + + // setting hidden view to hidden again is a programming error + // as it requires to have equal or more times of the opposite value to be set + // in order to reverse it + if (!viewToBeHidden.isHidden) { + viewToBeHidden.hidden = YES; + // decrease the intrinsic content size by the intrinsic content size of + // `viewToBeHidden` otherwise, viewTobeHidden's size will be included + [hostView decreaseIntrinsicContentSize:viewToBeHidden]; + [self changeVisiblityOfAssociatedViews:viewToBeHidden visibilityValue:YES contentStackView:hostView]; + } - if (isLastView && _paddingSet.count) { - [self changeVisibilitOfPaddingsTo:NO]; + // if `viewToBeHidden` is a head, get new head if any, and hide its separator + if (isHead) { + NSUInteger headIndex = [self getHeadIndexOfVisibleViews]; + if (headIndex != NSNotFound && headIndex < subviews.count) { + UIView *head = subviews[headIndex]; + [self changeVisiblityOfSeparator:head visibilityValue:YES contentStackView:hostView]; } } } -- (void)addPadding:(UIView *)padding { - if (padding && _paddingSet) { - [_paddingSet addObject:padding]; +/// unhide `viewToBeUnhidden`. `hostView` is a superview of type ColumnView or ColumnSetView +- (void)unhideView:(UIView *)viewToBeUnhidden hostView:(ACRContentStackView *)hostView +{ + if (!hostView || !viewToBeUnhidden || !_fillerSpaceManager) { + return; } -} -- (BOOL)isPadding:(UIView *)padding { - if (padding && _paddingSet) { - return [_paddingSet containsObject:padding]; + NSArray *subviews = [hostView getContentStackSubviews]; + if (!subviews) { + return; } - return NO; -} -- (void)changeVisibilitOfPaddingsTo:(BOOL)visiblity -{ - for (UIView *padding in _paddingSet) { - if (padding.isHidden != visiblity) { - padding.hidden = visiblity; + NSUInteger index = [subviews indexOfObject:viewToBeUnhidden]; + if (index == NSNotFound) { + return; + } + + NSUInteger headIndex = [self getHeadIndexOfVisibleViews]; + [self addVisibleView:index]; + // check if the unhidden view will become a head + if ([self amIHead:index]) { + // only enable filler view associated with the `viewTobeUnhidden` + [self changeVisibilityOfPadding:viewToBeUnhidden visibilityValue:NO]; + if (viewToBeUnhidden.isHidden) { + viewToBeUnhidden.hidden = NO; + [hostView increaseIntrinsicContentSize:viewToBeUnhidden]; + } + + // previous head view's separator becomes visible + if (headIndex != NSNotFound && headIndex < subviews.count) { + UIView *prevHeadView = subviews[headIndex]; + [self changeVisiblityOfSeparator:prevHeadView visibilityValue:NO contentStackView:hostView]; } + } else { + if (viewToBeUnhidden.isHidden) { + viewToBeUnhidden.hidden = NO; + [hostView increaseIntrinsicContentSize:viewToBeUnhidden]; + } + [self changeVisiblityOfAssociatedViews:viewToBeUnhidden visibilityValue:NO contentStackView:hostView]; } } +- (void)updatePaddingVisibility +{ + [_fillerSpaceManager deActivateConstraintsForPadding]; + [_fillerSpaceManager activateConstraintsForPadding]; +} @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm index 5984af1505..c5774d6714 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnRenderer.mm @@ -72,10 +72,10 @@ - (UIView *)render:(UIView *)viewGroup withCardElems:columnElem->GetItems() andHostConfig:acoConfig]; - [column configureHeight:GetACRVerticalContentAlignment(columnElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) - minHeight:columnElem->GetMinHeight() - heightType:GetACRHeight(columnElem->GetHeight()) - type:ACRColumn]; + [column configureLayoutAndVisibility:GetACRVerticalContentAlignment(columnElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) + minHeight:columnElem->GetMinHeight() + heightType:GetACRHeight(columnElem->GetHeight()) + type:ACRColumn]; [column setClipsToBounds:NO]; @@ -86,12 +86,6 @@ - (UIView *)render:(UIView *)viewGroup column.shouldGroupAccessibilityChildren = YES; - // config visibility for column view followed by configuring visibility of the items of column - configVisibility(column, elem); - configVisibilityWithVisibilityManager(rootView, column, column); - - [column hideIfSubviewsAreAllHidden]; - [viewGroup addArrangedSubview:column]; // viewGroup and column has to be in view hierarchy before configBleed is called diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm index f92703bc65..79d2ff363f 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetRenderer.mm @@ -62,18 +62,6 @@ - (UIView *)render:(UIView *)viewGroup NSMutableArray *constraints = [[NSMutableArray alloc] init]; - if (columnSetElem->GetMinHeight() > 0) { - [constraints addObject: - [NSLayoutConstraint constraintWithItem:columnSetView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:columnSetElem->GetMinHeight()]]; - constraints.lastObject.priority = 999; - } - ACRColumnRenderer *castedRenderer = (ACRColumnRenderer *)columnRenderer; auto relativeColumnWidthCounts = 0; @@ -110,12 +98,11 @@ - (UIView *)render:(UIView *)viewGroup UIView *viewWithMinWidth = nil; ACRColumnView *viewWithMaxSize = nil; NSMutableArray *viewsWithRelativeWidth = [[NSMutableArray alloc] init]; - NSMutableSet *viewsWithPaddingView = [[NSMutableSet alloc] init]; + NSMutableArray *columnViews = [[NSMutableArray alloc] init]; for (std::shared_ptr column : columns) { if (*firstColumn != column) { separator = [ACRSeparator renderSeparation:column forSuperview:columnSetView withHostConfig:config]; - configSeparatorVisibility(separator, prevColumn); } [acoColumn setElem:column]; @@ -132,6 +119,8 @@ - (UIView *)render:(UIView *)viewGroup curView = (ACRColumnView *)[columnRenderer render:columnSetView rootView:rootView inputs:inputs baseCardElement:acoColumn hostConfig:acoConfig]; if (separator && !curView) { [columnSetView removeViewFromContentStackView:separator]; + } else { + [columnSetView updateLayoutAndVisibilityOfRenderedView:curView acoElement:acoColumn separator:separator rootView:rootView]; } } @catch (ACOFallbackException *e) { @@ -190,10 +179,6 @@ - (UIView *)render:(UIView *)viewGroup [columnSetView setAlignmentForColumnStretch]; } -// if (curView.hasPaddingView) { -// [viewsWithPaddingView addObject:curView]; -// } - CGSize size = [curView intrinsicContentSize]; if (size.width * size.height > maxIntrinsicSize) { maxIntrinsicSize = size.width * size.height; @@ -203,11 +188,6 @@ - (UIView *)render:(UIView *)viewGroup prevColumn = column; } -// if (columns.size() > 1 && [viewsWithPaddingView containsObject:viewWithMaxSize]) { -// viewWithMaxSize.hasPaddingView = NO; -// [viewWithMaxSize removeLastViewFromArrangedSubview]; -// } - for (ACRColumnView *view in viewsWithRelativeWidth) { if (view != viewWithMinWidth && view.relativeWidth) { [constraints addObject: @@ -234,16 +214,16 @@ - (UIView *)render:(UIView *)viewGroup std::shared_ptr selectAction = columnSetElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; [columnSetView configureForSelectAction:acoSelectAction rootView:rootView]; - configVisibility(columnSetView, elem); - [columnSetView hideIfSubviewsAreAllHidden]; + [columnSetView configureLayoutAndVisibility:GetACRVerticalContentAlignment(columnSetElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) + minHeight:columnSetElem->GetMinHeight() + heightType:GetACRHeight(columnSetElem->GetHeight()) + type:ACRColumnSet]; [columnSetView setNeedsLayout]; [rootView.context popBaseCardElementContext:acoElem]; - [columnSetView toggleVisibilityOfFirstView]; - return columnSetView; } diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetView.mm index 913de380f1..c2fb0c72df 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnSetView.mm @@ -83,4 +83,25 @@ - (void)setAlignmentForColumnStretch super.alignment = UIStackViewAlignmentFill; } +- (void)configureLayoutAndVisibility:(ACRVerticalContentAlignment)verticalContentAlignment + minHeight:(NSInteger)minHeight + heightType:(ACRHeightType)heightType + type:(ACRCardElementType)type +{ + [super applyVisibilityToSubviews]; + + if (minHeight > 0) { + NSLayoutConstraint *constraint = + [NSLayoutConstraint constraintWithItem:self + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationGreaterThanOrEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1 + constant:minHeight]; + constraint.priority = 999; + constraint.active = YES; + } +} + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h index a110e7c0bc..2ed0390fe0 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.h @@ -4,12 +4,14 @@ // // Copyright © 2017 Microsoft. All rights reserved. // -#import "ACRContentStackView.h" +#import "ACOVisibilityManager.h" #import "ACRColumnSetView.h" +#import "ACRContentStackView.h" #import "ACRIBaseInputHandler.h" -#import "ACOVisibilityManager.h" -@interface ACRColumnView : ACRContentStackView +@class ACRSeparator; + +@interface ACRColumnView : ACRContentStackView typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { ACRColumnWidthPriorityStretch = 249, @@ -22,19 +24,8 @@ typedef NS_ENUM(NSInteger, ACRColumnWidthPriority) { @property CGFloat relativeWidth; @property ACRHeightType heightType; @property BOOL hasMoreThanOneRelativeWidth; -@property (nonatomic) BOOL hasStretchableView; @property BOOL isLastColumn; @property NSMutableArray *inputHandlers; @property (weak) ACRColumnSetView *columnsetView; -- (UIView *)addPaddingFor:(UIView *)view; - -- (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element; - -- (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment - minHeight:(NSInteger)minHeight - heightType:(ACRHeightType)heightType - type:(ACRCardElementType)type; - - @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm index e25ec1aea4..798649c561 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRColumnView.mm @@ -6,19 +6,14 @@ // #import "ACOBaseCardElementPrivate.h" -#import "ACOPaddingHandler.h" +#import "ACOFillerSpaceManager.h" #import "ACRView.h" -@implementation ACRColumnView { - ACOVisibilityManager *_visibilityManager; -@protected - ACOPaddingHandler *_paddingHandler; -} +@implementation ACRColumnView - (void)setColumnWidth:(NSString *)columnWidth { _columnWidth = columnWidth; - _visibilityManager.columnWidth = columnWidth; } - (void)config:(nullable NSDictionary *)attributes @@ -27,10 +22,7 @@ - (void)config:(nullable NSDictionary *)attributes [super config:attributes]; self.isLastColumn = NO; self.inputHandlers = [[NSMutableArray alloc] init]; - _visibilityManager = [[ACOVisibilityManager alloc] init]; - _paddingHandler = [[ACOPaddingHandler alloc] init]; } - - (void)addArrangedSubview:(UIView *)view { [self configureWidthOfView:view]; @@ -117,69 +109,4 @@ - (void)updateIntrinsicContentSize }]; } -- (void)hideView:(UIView *)view -{ - [_visibilityManager hideView:view arrangedSubviews:[self getContentStackSubviews]]; -} - -- (void)unhideView:(UIView *)view -{ - [_visibilityManager unhideView:view arrangedSubviews:[self getContentStackSubviews]]; -} - -- (BOOL)hasStretchableView -{ - return _paddingHandler.hasPadding; -} - -- (UIView *)addPaddingSpace -{ - UIView *padding = [super addPaddingSpace]; - [_visibilityManager addPadding:padding]; - return padding; -} - -- (UIView *)addPaddingFor:(UIView *)view -{ - return [_paddingHandler addPaddingFor:view]; -} - -- (void)configureHeightFor:(UIView *)view acoElement:(ACOBaseCardElement *)element -{ - [_paddingHandler configureHeight:view correspondingElement:element]; -} - -- (void)configureHeight:(ACRVerticalContentAlignment)verticalContentAlignment - minHeight:(NSInteger)minHeight - heightType:(ACRHeightType)heightType - type:(ACRCardElementType)type -{ - if (!self.hasStretchableView) { - if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || - verticalContentAlignment == ACRVerticalContentAlignmentBottom) { - [self insertArrangedSubview:[self addPaddingFor:self] atIndex:0]; - } - - if (verticalContentAlignment == ACRVerticalContentAlignmentCenter || - (verticalContentAlignment == ACRVerticalContentAlignmentTop && - self.distribution == UIStackViewDistributionFill)) { - [self addArrangedSubview:[self addPaddingFor:self]]; - } - } - if (minHeight > 0) { - NSLayoutConstraint *constraint = - [NSLayoutConstraint constraintWithItem:self - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationGreaterThanOrEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1 - constant:minHeight]; - constraint.priority = 999; - constraint.active = YES; - } - - [_paddingHandler activateConstraintsForPadding]; -} - @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm index 0aa3c0b556..a7e0d509da 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContainerRenderer.mm @@ -76,14 +76,11 @@ - (UIView *)render:(UIView *)viewGroup std::shared_ptr selectAction = containerElem->GetSelectAction(); ACOBaseActionElement *acoSelectAction = [ACOBaseActionElement getACOActionElementFromAdaptiveElement:selectAction]; [container configureForSelectAction:acoSelectAction rootView:rootView]; - configVisibility(container, elem); - - [container hideIfSubviewsAreAllHidden]; - - [container configureHeight:GetACRVerticalContentAlignment(containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) - minHeight:containerElem->GetMinHeight() - heightType:GetACRHeight(containerElem->GetHeight()) - type:ACRContainer]; + + [container configureLayoutAndVisibility:GetACRVerticalContentAlignment(containerElem->GetVerticalContentAlignment().value_or(VerticalContentAlignment::Top)) + minHeight:containerElem->GetMinHeight() + heightType:GetACRHeight(containerElem->GetHeight()) + type:ACRContainer]; [rootView.context popBaseCardElementContext:acoElem]; diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm index fc234d278f..075ce19fa6 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentHoldingUIView.mm @@ -219,4 +219,5 @@ - (NSLayoutConstraint *)setHeightConstraintUtil:(NSLayoutDimension *)heightAncho constraint.active = YES; return constraint; } + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.h b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.h index 0f43a5f534..a5c5770d46 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.h +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.h @@ -5,11 +5,17 @@ // Copyright © 2017 Microsoft. All rights reserved. // +#import "ACOVisibilityManager.h" #import "ACRIContentHoldingView.h" #import "ACRLongPressGestureRecognizerEventHandler.h" #import -@interface ACRContentStackView : UIView +@interface ACRContentStackView : UIView { + @protected + ACOFillerSpaceManager *_paddingHandler; + NSMutableArray *_paddings; + ACOVisibilityManager *_visibilityManager; +} @property (weak, nullable) UIView *backgroundView; @property (nonnull) NSArray *widthconstraint; @@ -24,6 +30,9 @@ @property BOOL isBackgroundImageSet; // if true, RTL's set @property ACRRtl rtl; +// there are some subviews that can be stretched +// it indicates that additional filler views are not needed. +@property (nonatomic) BOOL hasStretchableView; - (instancetype _Nonnull)initWithFrame:(CGRect)frame; @@ -65,7 +74,7 @@ - (void)updateIntrinsicContentSize; -- (void)updateIntrinsicContentSize:(void (^_Nonnull)(UIView * _Nonnull view, NSUInteger idx, BOOL * _Nonnull stop))block; +- (void)updateIntrinsicContentSize:(void (^_Nonnull)(UIView *_Nonnull view, NSUInteger idx, BOOL *_Nonnull stop))block; - (void)hideIfSubviewsAreAllHidden; @@ -75,4 +84,25 @@ - (UIView *_Nonnull)addPaddingSpace; +/// call this method once all subviews are rendered +/// this methods add padding to itself for alignment and stretch +/// apply visibility to subviews +/// configure min height +/// then activate all contraints associated with the configuration. +/// activation constraint all at once is more efficient than activating +/// constraints one by one. +- (void)configureLayoutAndVisibility:(ACRVerticalContentAlignment)verticalContentAlignment + minHeight:(NSInteger)minHeight + heightType:(ACRHeightType)heightType + type:(ACRCardElementType)type; + +/// call this method when visibility of subviews has chaned and +/// padding needs to be updated +- (void)updatePaddingVisibility; + +/// this method applies visibility to subviews once all of them are rendered and become part of content stack view +/// applying visibility as each subview is rendered has known side effects. +/// such as its superview, content stack view becomes hidden if a first subview is set hidden. +- (void)applyVisibilityToSubviews; + @end diff --git a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm index ce88cd7961..b485ad59a0 100644 --- a/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm +++ b/source/ios/AdaptiveCards/AdaptiveCards/AdaptiveCards/ACRContentStackView.mm @@ -6,6 +6,7 @@ // #include "ACRContentStackView.h" +#import "ACOBaseCardElementPrivate.h" #include "ACOHostConfigPrivate.h" #import "ACRShowCardTarget.h" #import "ACRViewPrivate.h" @@ -23,6 +24,8 @@ @implementation ACRContentStackView { NSHashTable *_hiddenSubviews; NSMutableDictionary *_subviewIntrinsicContentSizeCollection; ACRRtl _rtl; + NSMutableSet *_invisibleViews; + ACRVerticalContentAlignment _verticalContentAlignment; } - (instancetype)initWithStyle:(ACRContainerStyle)style @@ -54,6 +57,9 @@ - (instancetype)initWithFrame:(CGRect)frame attributes:(nullable NSDictionary