Files
2024-10-15 17:05:47 -04:00

594 lines
26 KiB
Objective-C

/*
Copyright (c) 2019, Apple Inc. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder(s) nor the names of any contributors
may be used to endorse or promote products derived from this software without
specific prior written permission. No license is granted to the trademarks of
the copyright holders even if such marks are included in this software.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "ORKBodyContainerView.h"
#import "ORKHelpers_Internal.h"
#import "ORKSkin.h"
#import "ORKBodyItem.h"
#import "ORKBodyItem_Internal.h"
#import "ORKLearnMoreView.h"
#import "ORKLearnMoreInstructionStep.h"
#import "ORKTagLabel.h"
static const CGFloat ORKBodyToBulletPaddingStandard = 26.0;
static const CGFloat ORKBulletToBulletPaddingStandard = 22.0;
static const CGFloat ORKBodyToLearnMorePaddingStandard = 15.0;
static const CGFloat ORKTagLabelBottomPadding = 8.0;
static const CGFloat ORKHorizontalRulePadding = 22.0;
static const CGFloat ORKBodyTextToBodyDetailTextPaddingStandard = 6.0;
static const CGFloat ORKBodyTextToLearnMoreButtonPaddingStandard = 15.0;
static const CGFloat ORKBodyDetailTextToLearnMoreButtonPaddingStandard = 15.0;
static const CGFloat ORKBulletIconToBodyPadding = 14.0;
static const CGFloat ORKBulletIconWidthStandard = 10.0;
static const CGFloat ORKBulletIconDimension = 40.0;
static const CGFloat ORKCardStylePadding = 16.0;
static const CGFloat ORKCardStyleMediumTextPadding = 14.0;
static const CGFloat ORKCardStyleSmallTextPadding = 2.0;
static const CGFloat ORKCardStyleBuildInPostitionStart = 31.0;
static const CGFloat ORKCardStyleBuildInPostitionEnd = 26.0;
static NSString *ORKBulletUnicode = @"\u2981";
//static const CGFloat ORKBulletToBulletPaddingShort = 22.0;
//static const CGFloat ORKBulletToBulletPaddingGenerous = 36.0;
//static const CGFloat ORKBodyToBulletPaddingShort = 22.0;
@protocol ORKBodyItemViewDelegate <NSObject>
@required
- (void)bodyItemLearnMoreButtonPressed:(ORKLearnMoreInstructionStep *)learnMoreStep;
@end
@interface ORKBodyItemView: UIStackView
- (instancetype)initWithBodyItem:(ORKBodyItem *)bodyItem;
@property (nonatomic, nonnull) ORKBodyItem *bodyItem;
@property (nonatomic, weak) id<ORKBodyItemViewDelegate> delegate;
@property (nonatomic, strong) UIView *cardView;
@property (nonatomic, strong) UIView *cardStyleAccessoryView;
@end
@interface ORKBodyItemView()<ORKLearnMoreViewDelegate>
@property (nonatomic) NSTextAlignment textAlignment;
@end
@implementation ORKBodyItemView
- (instancetype)initWithBodyItem:(ORKBodyItem *)bodyItem {
self = [super init];
if (self) {
self.bodyItem = bodyItem;
self.textAlignment = NSTextAlignmentLeft;
[self setupBodyStyleView];
}
return self;
}
- (instancetype)initWithBodyItem:(ORKBodyItem *)bodyItem textAlignment:(NSTextAlignment)textAlignment {
self = [super init];
if (self) {
self.bodyItem = bodyItem;
self.textAlignment = textAlignment;
[self setupBodyStyleView];
}
return self;
}
- (void)setupBodyStyleView {
if (_bodyItem.useCardStyle == YES) {
_cardView = [[UIView alloc] init];
_cardView.translatesAutoresizingMaskIntoConstraints = NO;
[self addArrangedSubview:_cardView];
}
if ([_bodyItem isCustomButtonItemType])
{
[self setupCustomButtonViewWithConfigurationHandler:_bodyItem.customButtonConfigurationHandler];
}
if (_bodyItem.bodyItemStyle == ORKBodyItemStyleText) {
[self setupBodyStyleTextView];
} else if (_bodyItem.bodyItemStyle == ORKBodyItemStyleImage) {
[self setupBulletPointStackView];
[self setupBodyStyleImage];
} else if (_bodyItem.bodyItemStyle == ORKBodyItemStyleBulletPoint) {
[self setupBulletPointStackView];
[self setupBodyStyleBulletPointView];
} else if (_bodyItem.bodyItemStyle == ORKBodyItemStyleHorizontalRule) {
[self setupBodyStyleHorizontalRule];
} else if (_bodyItem.bodyItemStyle == ORKBodyItemStyleTag) {
[self setupBodyStyleTag];
}
}
+ (UIFont *)bodyTitleFontBold {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
UIFontDescriptor *fontDescriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitBold)];
return [UIFont fontWithDescriptor:fontDescriptor size:[[fontDescriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bodyTitleFont {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
return [UIFont fontWithDescriptor:descriptor size:[[descriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bodyTextFont {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
return [UIFont fontWithDescriptor:descriptor size:[[descriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bulletIconFont {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];
descriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitTightLeading | UIFontDescriptorTraitBold)];
return [UIFont fontWithDescriptor:descriptor size:0];
}
+ (UIFont *)bulletTextFontBold {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
UIFontDescriptor *fontDescriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitBold | UIFontDescriptorTraitLooseLeading)];
return [UIFont fontWithDescriptor:fontDescriptor size:[[fontDescriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bulletBodyTextFontBold {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody];
UIFontDescriptor *fontDescriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitBold | UIFontDescriptorTraitLooseLeading)];
return [UIFont fontWithDescriptor:fontDescriptor size:[[fontDescriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bulletTextFont {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];
UIFontDescriptor *fontDescriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitLooseLeading)];
return [UIFont fontWithDescriptor:fontDescriptor size:[[fontDescriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
+ (UIFont *)bulletDetailTextFont {
UIFontDescriptor *descriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleSubheadline];
UIFontDescriptor *fontDescriptor = [descriptor fontDescriptorWithSymbolicTraits:(UIFontDescriptorTraitLooseLeading)];
return [UIFont fontWithDescriptor:fontDescriptor size:[[descriptor objectForKey: UIFontDescriptorSizeAttribute] doubleValue]];
}
- (void)setupCustomButtonViewWithConfigurationHandler:(void(^)(UIButton *button))configurationHandler
{
UIButton *button = [[UIButton alloc] init];
configurationHandler(button);
button.translatesAutoresizingMaskIntoConstraints = NO;
[self addArrangedSubview:button];
[NSLayoutConstraint activateConstraints:@[
[button.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:ORKStepContainerLeftRightPaddingForWindow(self.window)]
]];
}
- (void)setupBodyStyleHorizontalRule {
self.axis = UILayoutConstraintAxisVertical;
self.distribution = UIStackViewDistributionFill;
UIView *separator = [UIView new];
separator.translatesAutoresizingMaskIntoConstraints = NO;
separator.backgroundColor = UIColor.separatorColor;
[separator.heightAnchor constraintEqualToConstant:1.0 / [UIScreen mainScreen].scale].active = YES;
[self addArrangedSubview:separator];
}
- (void)setupBodyStyleTextView {
self.axis = UILayoutConstraintAxisVertical;
self.distribution = UIStackViewDistributionFill;
self.alignment = self.textAlignment == NSTextAlignmentCenter ? UIStackViewAlignmentCenter : UIStackViewAlignmentLeading;
UILabel *textLabel;
UILabel *detailTextLabel;
if (_bodyItem.text) {
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.font = _bodyItem.detailText == nil ? [ORKBodyItemView bodyTitleFont] : [ORKBodyItemView bodyTitleFontBold];
textLabel.text = _bodyItem.text;
textLabel.textAlignment = _textAlignment;
textLabel.translatesAutoresizingMaskIntoConstraints = NO;
if (_bodyItem.useCardStyle == YES) {
[_cardView addSubview:textLabel];
[textLabel.leadingAnchor constraintEqualToAnchor: _cardView.leadingAnchor constant:ORKCardStyleMediumTextPadding].active = YES;
[textLabel.topAnchor constraintEqualToAnchor:_cardView.topAnchor constant:ORKCardStylePadding].active = YES;
[textLabel.trailingAnchor constraintEqualToAnchor:_cardView.trailingAnchor constant:-ORKCardStylePadding].active = YES;
if (_bodyItem.detailText == nil) {
[textLabel.bottomAnchor constraintEqualToAnchor:_cardView.bottomAnchor constant:-ORKCardStylePadding].active = YES;
}
} else {
[self addArrangedSubview:textLabel];
}
}
if (_bodyItem.detailText) {
detailTextLabel = [UILabel new];
detailTextLabel.numberOfLines = 0;
detailTextLabel.font = [ORKBodyItemView bodyTextFont];
detailTextLabel.text = _bodyItem.detailText;
detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
if (_bodyItem.useCardStyle == YES) {
[_cardView addSubview:detailTextLabel];
[detailTextLabel.leadingAnchor constraintEqualToAnchor: _cardView.leadingAnchor constant:ORKCardStyleMediumTextPadding].active = YES;
[detailTextLabel.topAnchor constraintEqualToAnchor:textLabel.bottomAnchor constant:ORKCardStyleSmallTextPadding].active = YES;
[detailTextLabel.trailingAnchor constraintEqualToAnchor:_cardView.trailingAnchor constant:-ORKCardStylePadding].active = YES;
[detailTextLabel.bottomAnchor constraintEqualToAnchor:_cardView.bottomAnchor constant:-ORKCardStylePadding].active = YES;
} else {
[self addArrangedSubview:detailTextLabel];
if (textLabel) {
[self setCustomSpacing:ORKBodyTextToBodyDetailTextPaddingStandard afterView:textLabel];
}
}
}
if (_bodyItem.learnMoreItem) {
ORKLearnMoreView *learnMoreView = _bodyItem.learnMoreItem.text ? [ORKLearnMoreView learnMoreCustomButtonViewWithText:_bodyItem.learnMoreItem.text LearnMoreInstructionStep:_bodyItem.learnMoreItem.learnMoreInstructionStep] : [ORKLearnMoreView learnMoreDetailDisclosureButtonViewWithLearnMoreInstructionStep:_bodyItem.learnMoreItem.learnMoreInstructionStep];
[learnMoreView setLearnMoreButtonFont:[ORKBodyItemView bodyTextFont]];
[learnMoreView setLearnMoreButtonTextAlignment:_textAlignment];
learnMoreView.delegate = _bodyItem.learnMoreItem.delegate ? : self;
learnMoreView.translatesAutoresizingMaskIntoConstraints = NO;
[self addArrangedSubview:learnMoreView];
if (detailTextLabel) {
[self setCustomSpacing:ORKBodyDetailTextToLearnMoreButtonPaddingStandard afterView:detailTextLabel];
}
else if (textLabel) {
[self setCustomSpacing:ORKBodyTextToLearnMoreButtonPaddingStandard afterView:textLabel];
}
}
}
- (void)setupBulletPointStackView {
self.axis = UILayoutConstraintAxisHorizontal;
self.layoutMargins = UIEdgeInsetsZero;
[self setLayoutMarginsRelativeArrangement:YES];
}
- (void)setupBodyStyleBulletPointView {
UILabel *bulletIcon = [self bulletIcon];
if (_bodyItem.useCardStyle == YES) {
bulletIcon.translatesAutoresizingMaskIntoConstraints = NO;
[_cardView addSubview:bulletIcon];
[bulletIcon.leadingAnchor constraintEqualToAnchor:_cardView.leadingAnchor].active = YES;
[bulletIcon.topAnchor constraintEqualToAnchor:_cardView.topAnchor].active = YES;
_cardStyleAccessoryView = bulletIcon;
} else {
[self addArrangedSubview:bulletIcon]; // Stack this in substack for vertical bullet icon.
[self setCustomSpacing:ORKBulletIconToBodyPadding afterView:bulletIcon];
}
[self addSubStackView];
}
- (void)setupBodyStyleImage {
UIImageView *imageView = [self imageView];
self.alignment = _bodyItem.alignImageToTop ? UIStackViewAlignmentTop : UIStackViewAlignmentCenter;
if (_bodyItem.useCardStyle == YES) {
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[_cardView addSubview:imageView];
[imageView.leadingAnchor constraintEqualToAnchor:_cardView.leadingAnchor].active = YES;
[imageView.centerYAnchor constraintEqualToAnchor:_cardView.centerYAnchor].active = YES;
[_cardView.heightAnchor constraintGreaterThanOrEqualToAnchor:imageView.heightAnchor].active = YES;
_cardStyleAccessoryView = imageView;
} else {
[self addArrangedSubview:imageView];
[self setCustomSpacing:ORKBulletIconToBodyPadding afterView:imageView];
}
[self addSubStackView];
}
- (UILabel *)bulletIcon {
UILabel *bulletIconLabel = [UILabel new];
bulletIconLabel.numberOfLines = 1;
bulletIconLabel.font = [ORKBodyItemView bulletIconFont];
bulletIconLabel.textColor = [UIColor secondaryLabelColor];
[bulletIconLabel setText:ORKBulletUnicode];
bulletIconLabel.translatesAutoresizingMaskIntoConstraints = NO;
[[bulletIconLabel.widthAnchor constraintGreaterThanOrEqualToConstant:ORKBulletIconWidthStandard] setActive:YES];
return bulletIconLabel;
}
- (UIImageView *)imageView {
UIImageView *imageView = [UIImageView new];
imageView.image = self.bodyItem.image;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
if (self.bodyItem.useSecondaryColor) {
imageView.tintColor = UIColor.grayColor;
}
// To allow symbols to handle their own configuration
if (imageView.image.isSymbolImage) {
imageView.contentMode = UIViewContentModeCenter;
} else {
imageView.contentMode = UIViewContentModeScaleAspectFit;
}
[imageView.heightAnchor constraintEqualToConstant:ORKBulletIconDimension].active = YES;
[imageView.widthAnchor constraintEqualToConstant:ORKBulletIconDimension].active = YES;
return imageView;
}
- (void)setupBodyStyleTag {
UIView *container = [UIView new];
container.translatesAutoresizingMaskIntoConstraints = NO;
ORKTagLabel *tagLabel = [ORKTagLabel new];
tagLabel.translatesAutoresizingMaskIntoConstraints = NO;
if (_bodyItem.image != nil) {
NSMutableAttributedString *iconText = [self icon:_bodyItem.image withText:_bodyItem.text font:tagLabel.font color:tagLabel.textColor];
if (iconText != nil) {
tagLabel.attributedText = iconText;
} else {
tagLabel.text = _bodyItem.text;
}
} else {
tagLabel.text = _bodyItem.text;
}
[container addSubview:tagLabel];
[tagLabel.topAnchor constraintEqualToAnchor:container.topAnchor].active = YES;
[tagLabel.leadingAnchor constraintEqualToAnchor:container.leadingAnchor].active = YES;
[tagLabel.bottomAnchor constraintEqualToAnchor:container.bottomAnchor].active = YES;
[tagLabel.trailingAnchor constraintLessThanOrEqualToAnchor:container.trailingAnchor].active = YES;
[self addArrangedSubview:container];
}
- (void)addSubStackView {
UILabel *textLabel;
UILabel *detailTextLabel;
UIStackView *subStackView = [[UIStackView alloc] init];
if (_bodyItem.useCardStyle == NO) {
subStackView.axis = UILayoutConstraintAxisVertical;
subStackView.distribution = UIStackViewDistributionFill;
subStackView.alignment = UIStackViewAlignmentLeading;
subStackView.translatesAutoresizingMaskIntoConstraints = NO;
[self addArrangedSubview:subStackView];
}
if (_bodyItem.text) {
textLabel = [UILabel new];
textLabel.numberOfLines = 0;
textLabel.text = _bodyItem.text;
textLabel.translatesAutoresizingMaskIntoConstraints = NO;
if (_bodyItem.useCardStyle == YES) {
textLabel.font = [ORKBodyItemView bulletBodyTextFontBold];
[_cardStyleAccessoryView setContentHuggingPriority:UILayoutPriorityDefaultHigh forAxis:UILayoutConstraintAxisHorizontal];
[_cardView addSubview:textLabel];
[textLabel.leadingAnchor constraintEqualToAnchor: _cardStyleAccessoryView.trailingAnchor constant:ORKCardStyleMediumTextPadding].active = YES;
[textLabel.topAnchor constraintEqualToAnchor:_cardView.topAnchor constant:0].active = YES;
[textLabel.trailingAnchor constraintEqualToAnchor:_cardView.trailingAnchor].active = YES;
[textLabel setContentHuggingPriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
if (_bodyItem.detailText == nil) {
[textLabel.bottomAnchor constraintEqualToAnchor:_cardView.bottomAnchor].active = YES;
}
} else {
textLabel.font = _bodyItem.detailText ? [ORKBodyItemView bulletTextFontBold] : [ORKBodyItemView bulletTextFont];
[subStackView addArrangedSubview:textLabel];
}
}
if (_bodyItem.detailText) {
detailTextLabel = [UILabel new];
detailTextLabel.numberOfLines = 0;
detailTextLabel.font = [ORKBodyItemView bulletDetailTextFont];
detailTextLabel.text = _bodyItem.detailText;
if (_bodyItem.useCardStyle == YES) {
[detailTextLabel setTextColor:[UIColor labelColor]];
} else {
[detailTextLabel setTextColor:ORKColor(ORKBulletItemTextColorKey)];
}
detailTextLabel.translatesAutoresizingMaskIntoConstraints = NO;
if (_bodyItem.useCardStyle == YES) {
[_cardView addSubview:detailTextLabel];
[detailTextLabel.leadingAnchor constraintEqualToAnchor: _cardStyleAccessoryView.trailingAnchor constant:ORKCardStyleMediumTextPadding].active = YES;
[detailTextLabel.topAnchor constraintEqualToAnchor:textLabel.bottomAnchor constant:ORKCardStyleSmallTextPadding].active = YES;
[detailTextLabel.trailingAnchor constraintEqualToAnchor:_cardView.trailingAnchor].active = YES;
[detailTextLabel.bottomAnchor constraintEqualToAnchor:_cardView.bottomAnchor].active = YES;
} else {
[subStackView addArrangedSubview:detailTextLabel];
}
}
if (_bodyItem.learnMoreItem) {
ORKLearnMoreView *learnMoreView = _bodyItem.learnMoreItem.text ? [ORKLearnMoreView learnMoreCustomButtonViewWithText:_bodyItem.learnMoreItem.text LearnMoreInstructionStep:_bodyItem.learnMoreItem.learnMoreInstructionStep] : [ORKLearnMoreView learnMoreDetailDisclosureButtonViewWithLearnMoreInstructionStep:_bodyItem.learnMoreItem.learnMoreInstructionStep];
[learnMoreView setLearnMoreButtonFont:[ORKBodyItemView bulletDetailTextFont]];
learnMoreView.delegate = self;
learnMoreView.translatesAutoresizingMaskIntoConstraints = NO;
[subStackView addArrangedSubview:learnMoreView];
}
}
- (NSMutableAttributedString *)icon:(UIImage *)image withText:(NSString *)text font:(UIFont *)font color:(UIColor *)color {
NSString *textString = [[NSString alloc] initWithFormat:@" %@", text];
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:textString];
NSTextAttachment *imageAttachment = [[NSTextAttachment alloc] init];
UIImage *iconImage = image;
if (iconImage.isSymbolImage) {
UIImageSymbolConfiguration *configuration = [UIImageSymbolConfiguration configurationWithFont:font];
iconImage = [[image imageWithConfiguration:configuration] imageWithTintColor:color];
}
imageAttachment.image = iconImage;
NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:imageAttachment];
[attributedText insertAttributedString:imageString atIndex:0];
return attributedText;
}
#pragma mark - ORKLearnMoreViewDelegate
- (void)learnMoreButtonPressedWithStep:(ORKLearnMoreInstructionStep *)learnMoreStep {
[_delegate bodyItemLearnMoreButtonPressed:learnMoreStep];
}
@end
@interface ORKBodyContainerView()<ORKBodyItemViewDelegate>
@property (nonatomic, strong) NSArray<ORKBodyItemView *> *views;
@property (nonatomic) NSTextAlignment textAlignment;
@property (nonatomic) NSUInteger currentBodyItemIndex;
@end
@implementation ORKBodyContainerView
- (instancetype)initWithBodyItems:(NSArray<ORKBodyItem *> *)bodyItems
textAlignment:(NSTextAlignment)textAlignment
delegate:(nonnull id<ORKBodyContainerViewDelegate>)delegate {
self.delegate = delegate;
if (bodyItems && bodyItems.count <= 0) {
NSAssert(NO, @"Body Items array cannot be empty");
}
self = [super init];
if (self) {
self.bodyItems = bodyItems;
self.textAlignment = textAlignment;
self.axis = UILayoutConstraintAxisVertical;
self.distribution = UIStackViewDistributionFill;
[self addBodyItemViews];
}
return self;
}
- (void)addBodyItemViews {
_views = [ORKBodyContainerView bodyItemViewsWithBodyItems:_bodyItems textAlignment:_textAlignment];
for (NSInteger i = 0; i < _views.count; i++) {
[self addArrangedSubview:_views[i]];
_views[i].delegate = self;
if (i < _views.count - 1) {
CGFloat padding = [self spacingWithAboveStyle:_bodyItems[i].bodyItemStyle belowStyle:_bodyItems[i + 1].bodyItemStyle belowIsLearnMore:(_bodyItems[i + 1].learnMoreItem != nil) belowItemIndex:i+1];
[self setCustomSpacing:padding afterView:_views[i]];
}
}
}
- (void)setBuildsInBodyItems:(BOOL)buildsInBodyItems {
_buildsInBodyItems = buildsInBodyItems;
if (buildsInBodyItems == YES) {
for (NSInteger i = 0; i < _views.count; i++) {
[self setCustomSpacing:ORKCardStyleBuildInPostitionStart afterView:_views[i]];
if ((_buildsInBodyItems == YES) && (i != 0)) {
_views[i].alpha = 0;
}
}
_currentBodyItemIndex = 0;
}
}
- (void)updateBodyItemViews {
if (_buildsInBodyItems == NO) { return; }
NSUInteger indexToShow = _currentBodyItemIndex + 1;
for (NSInteger i = 0; i < _views.count; i++) {
if (i == indexToShow) {
[UIView transitionWithView:_views[i] duration:0.5 options:UIViewAnimationOptionTransitionCrossDissolve animations:^ {
[self setCustomSpacing:ORKCardStyleBuildInPostitionEnd afterView:_views[i - 1]];
_views[i].alpha = 1;
} completion:nil];
}
}
_currentBodyItemIndex++;
}
- (BOOL)hasShownAllBodyItem {
return (_currentBodyItemIndex == (_views.count - 1));
}
- (UIView *)lastVisibleBodyItem {
return _views[_currentBodyItemIndex];
}
- (void)layoutSubviews {
[super layoutSubviews];
if ([_bodyItemDelegate respondsToSelector:@selector(bodyContainerViewDidLoadBodyItems)]) {
[_bodyItemDelegate bodyContainerViewDidLoadBodyItems];
}
}
- (CGFloat)spacingWithAboveStyle:(ORKBodyItemStyle)aboveStyle belowStyle:(ORKBodyItemStyle )belowStyle belowIsLearnMore:(BOOL)belowIsLearnMore belowItemIndex:(NSInteger)belowItemIndex {
if (aboveStyle == ORKBodyItemStyleHorizontalRule || belowStyle == ORKBodyItemStyleHorizontalRule) {
return ORKHorizontalRulePadding;
} else if (belowIsLearnMore == YES) {
return ORKBodyToLearnMorePaddingStandard;
} else if (aboveStyle == ORKBodyItemStyleText) {
return belowStyle == ORKBodyItemStyleText ? (_bodyItems[belowItemIndex].text ? ORKBodyToBodyParagraphPaddingStandard : ORKBodyToBodyPaddingStandard) : ORKBodyToBulletPaddingStandard;
} else if (aboveStyle == ORKBodyItemStyleTag) {
if (belowStyle == ORKBodyItemStyleBulletPoint) {
return ORKBulletToBulletPaddingStandard;
} else {
return ORKTagLabelBottomPadding;
}
} else {
return belowStyle == ORKBodyItemStyleText ? ORKBodyToBulletPaddingStandard : ORKBulletToBulletPaddingStandard;
}
}
+ (NSArray<ORKBodyItemView *> *)bodyItemViewsWithBodyItems:(NSArray<ORKBodyItem *> *)bodyItems textAlignment:(NSTextAlignment)textAlignment {
NSMutableArray<ORKBodyItemView *> *viewsArray = [[NSMutableArray alloc] init];
[bodyItems enumerateObjectsUsingBlock:^(ORKBodyItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
ORKBodyItemView *itemView = [[ORKBodyItemView alloc] initWithBodyItem:obj textAlignment:textAlignment];
itemView.translatesAutoresizingMaskIntoConstraints = NO;
[viewsArray addObject:itemView];
}];
return [viewsArray copy];
}
#pragma mark - ORKBodyItemViewDelegate
- (void)bodyItemLearnMoreButtonPressed:(ORKLearnMoreInstructionStep *)learnMoreStep {
[_delegate bodyContainerLearnMoreButtonPressed:learnMoreStep];
}
@end