645 lines
26 KiB
Objective-C
645 lines
26 KiB
Objective-C
/*
|
|
Copyright (c) 2015, 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 "ORKStepViewController_Internal.h"
|
|
|
|
#import "ORKStepViewController_Internal.h"
|
|
#import "ORKTaskViewController_Internal.h"
|
|
|
|
#import "ORKCollectionResult.h"
|
|
#import "ORKReviewStep_Internal.h"
|
|
|
|
#import "ORKNavigationContainerView.h"
|
|
|
|
#import "ORKHelpers_Internal.h"
|
|
#import "ORKSkin.h"
|
|
#import "ORKStepContentView.h"
|
|
#import "ORKEarlyTerminationConfiguration.h"
|
|
#import "UIBarButtonItem+ORKBarButtonItem.h"
|
|
|
|
static const CGFloat iPadStepTitleLabelPadding = 15.0;
|
|
static const CGFloat iPadStepTitleLabelFontSize = 50.0;
|
|
|
|
@interface ORKStepViewController () {
|
|
BOOL _hasBeenPresented;
|
|
BOOL _dismissing;
|
|
BOOL _presentingAlert;
|
|
}
|
|
|
|
@property (nonatomic, strong,readonly) UIBarButtonItem *flexSpace;
|
|
@property (nonatomic, strong,readonly) UIBarButtonItem *fixedSpace;
|
|
|
|
@end
|
|
|
|
|
|
@implementation ORKStepViewController {
|
|
UIView *_iPadBackgroundView;
|
|
UIView *_iPadContentView;
|
|
UILabel *_iPadStepTitleLabel;
|
|
NSArray<NSLayoutConstraint *> *_iPadConstraints;
|
|
}
|
|
|
|
- (void)initializeInternalButtonItems {
|
|
_internalContinueButtonItem = [[UIBarButtonItem alloc] initWithTitle:ORKLocalizedString(@"BUTTON_NEXT", nil) style:UIBarButtonItemStylePlain target:self action:@selector(goForward)];
|
|
_internalDoneButtonItem = [[UIBarButtonItem alloc] initWithTitle:ORKLocalizedString(@"BUTTON_DONE", nil) style:UIBarButtonItemStyleDone target:self action:@selector(goForward)];
|
|
_internalSkipButtonItem = [[UIBarButtonItem alloc] initWithTitle:ORKLocalizedString(@"BUTTON_SKIP", nil) style:UIBarButtonItemStylePlain target:self action:@selector(skip:)];
|
|
_backButtonItem = nil;
|
|
}
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wobjc-designated-initializers"
|
|
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
|
|
self = [super initWithCoder:aDecoder];
|
|
if (self) {
|
|
[self initializeInternalButtonItems];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
- (instancetype)initWithStep:(ORKStep *)step {
|
|
self = [super initWithNibName:nil bundle:nil];
|
|
if (self) {
|
|
_wasSkipped = false;
|
|
[self initializeInternalButtonItems];
|
|
[self setStep:step];
|
|
self.restorationIdentifier = step.identifier;
|
|
self.restorationClass = self.class;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithStep:(ORKStep *)step result:(ORKResult *)result {
|
|
// The default implementation ignores the previous result.
|
|
return [self initWithStep:step];
|
|
}
|
|
|
|
- (void)viewDidLoad {
|
|
[super viewDidLoad];
|
|
|
|
self.view.backgroundColor = ORKColor(ORKBackgroundColorKey);
|
|
if (!_shouldIgnoreiPadDesign && ORKNeedWideScreenDesign(self.view)) {
|
|
[self setupiPadBackgroundView];
|
|
[self setupiPadContentView];
|
|
[self setupiPadStepTitleLabel];
|
|
[self setupiPadConstraints];
|
|
}
|
|
}
|
|
|
|
- (void)updateBarButtonItems {
|
|
if (self.shouldPresentInReview) {
|
|
self.navigationItem.leftBarButtonItem = self.cancelButtonItem;
|
|
self.navigationItem.rightBarButtonItem = self.internalDoneButtonItem;
|
|
} else {
|
|
self.navigationItem.leftBarButtonItem = self.backButtonItem;
|
|
self.navigationItem.rightBarButtonItem = self.cancelButtonItem;
|
|
}
|
|
}
|
|
|
|
- (void)setShouldPresentInReview:(BOOL)shouldPresentInReview {
|
|
_shouldPresentInReview = shouldPresentInReview;
|
|
[self updateBarButtonItems];
|
|
}
|
|
|
|
- (void)setupiPadBackgroundView {
|
|
if (!_iPadBackgroundView) {
|
|
_iPadBackgroundView = [UIView new];
|
|
}
|
|
[self.view addSubview:_iPadBackgroundView];
|
|
}
|
|
|
|
- (void)setupiPadContentView {
|
|
if (!_iPadContentView) {
|
|
_iPadContentView = [UIView new];
|
|
}
|
|
[_iPadBackgroundView addSubview:_iPadContentView];
|
|
}
|
|
|
|
- (void)setupiPadStepTitleLabel {
|
|
if (!_iPadStepTitleLabel) {
|
|
_iPadStepTitleLabel = [UILabel new];
|
|
}
|
|
_iPadStepTitleLabel.numberOfLines = 0;
|
|
_iPadStepTitleLabel.textAlignment = NSTextAlignmentNatural;
|
|
[_iPadStepTitleLabel setFont:[UIFont systemFontOfSize:iPadStepTitleLabelFontSize weight:UIFontWeightBold]];
|
|
[_iPadStepTitleLabel setAdjustsFontSizeToFitWidth:YES];
|
|
[_iPadBackgroundView addSubview:_iPadStepTitleLabel];
|
|
}
|
|
|
|
- (void)setiPadStepTitleLabelText:(NSString *)text {
|
|
if (_iPadStepTitleLabel) {
|
|
// [_iPadStepTitleLabel setText: text];
|
|
}
|
|
}
|
|
|
|
- (void)setiPadBackgroundViewColor:(UIColor *)color {
|
|
if (_iPadBackgroundView) {
|
|
// [_iPadBackgroundView setBackgroundColor:color];
|
|
}
|
|
}
|
|
|
|
- (void)setupiPadConstraints {
|
|
_iPadBackgroundView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
_iPadContentView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
_iPadStepTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
|
|
if (_iPadConstraints) {
|
|
[NSLayoutConstraint deactivateConstraints:_iPadConstraints];
|
|
}
|
|
|
|
_iPadConstraints = @[
|
|
[NSLayoutConstraint constraintWithItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeTop
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:self.view.safeAreaLayoutGuide
|
|
attribute:NSLayoutAttributeTop
|
|
multiplier:1.0
|
|
constant:0.0],
|
|
[NSLayoutConstraint constraintWithItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeLeft
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:self.view.safeAreaLayoutGuide
|
|
attribute:NSLayoutAttributeLeft
|
|
multiplier:1.0
|
|
constant:ORKiPadBackgroundViewLeftRightPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeRight
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:self.view.safeAreaLayoutGuide
|
|
attribute:NSLayoutAttributeRight
|
|
multiplier:1.0
|
|
constant:-ORKiPadBackgroundViewLeftRightPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeBottom
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:self.view.safeAreaLayoutGuide
|
|
attribute:NSLayoutAttributeBottom
|
|
multiplier:1.0
|
|
constant:-ORKiPadBackgroundViewBottomPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadStepTitleLabel
|
|
attribute:NSLayoutAttributeTop
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeTop
|
|
multiplier:1.0
|
|
constant:ORKiPadBackgroundViewBottomPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadStepTitleLabel
|
|
attribute:NSLayoutAttributeLeft
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeLeft
|
|
multiplier:1.0
|
|
constant:iPadStepTitleLabelPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadStepTitleLabel
|
|
attribute:NSLayoutAttributeRight
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeRight
|
|
multiplier:1.0
|
|
constant:-iPadStepTitleLabelPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadContentView
|
|
attribute:NSLayoutAttributeTop
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadStepTitleLabel
|
|
attribute:NSLayoutAttributeBottom
|
|
multiplier:1.0
|
|
constant:iPadStepTitleLabelPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadContentView
|
|
attribute:NSLayoutAttributeLeft
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeLeft
|
|
multiplier:1.0
|
|
constant:iPadStepTitleLabelPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadContentView
|
|
attribute:NSLayoutAttributeRight
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeRight
|
|
multiplier:1.0
|
|
constant:-iPadStepTitleLabelPadding],
|
|
[NSLayoutConstraint constraintWithItem:_iPadContentView
|
|
attribute:NSLayoutAttributeBottom
|
|
relatedBy:NSLayoutRelationEqual
|
|
toItem:_iPadBackgroundView
|
|
attribute:NSLayoutAttributeBottom
|
|
multiplier:1.0
|
|
constant:-iPadStepTitleLabelPadding]
|
|
];
|
|
[NSLayoutConstraint activateConstraints:_iPadConstraints];
|
|
}
|
|
|
|
- (void)setupButtons {
|
|
if (self.hasNextStep == YES) {
|
|
self.continueButtonItem = _internalContinueButtonItem;
|
|
} else {
|
|
self.continueButtonItem = _internalDoneButtonItem;
|
|
}
|
|
|
|
self.skipButtonItem = _internalSkipButtonItem;
|
|
}
|
|
|
|
- (void)setStep:(ORKStep *)step {
|
|
if (_hasBeenPresented) {
|
|
@throw [NSException exceptionWithName:NSGenericException reason:@"Cannot set step after presenting step view controller" userInfo:nil];
|
|
}
|
|
if (step && step.identifier == nil) {
|
|
ORK_Log_Debug("Step identifier should not be nil.");
|
|
}
|
|
|
|
_step = step;
|
|
|
|
[step validateParameters];
|
|
|
|
[self setupButtons];
|
|
[self stepDidChange];
|
|
}
|
|
|
|
- (void)stepDidChange {
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated {
|
|
[super viewWillAppear:animated];
|
|
ORK_Log_Debug("%@", self);
|
|
|
|
|
|
// Required here (instead of viewDidLoad) because any custom buttons are set once the delegate responds to the stepViewControllerWillAppear,
|
|
// otherwise there is a minor visual glitch, where the original buttons are displayed on the UI for a short period. This is not placed after
|
|
// the delegate responds to the stepViewControllerWillAppear, so that the target from the button's item can be used, if the intention is to
|
|
// only modify the title of the button.
|
|
[self setupButtons];
|
|
|
|
if ([self.delegate respondsToSelector:@selector(stepViewControllerWillAppear:)]) {
|
|
[self.delegate stepViewControllerWillAppear:self];
|
|
}
|
|
|
|
if (!_step) {
|
|
@throw [NSException exceptionWithName:NSGenericException reason:@"Cannot present step view controller without a step" userInfo:nil];
|
|
}
|
|
_hasBeenPresented = YES;
|
|
|
|
// Set presentedDate on first time viewWillAppear
|
|
if (!self.presentedDate) {
|
|
self.presentedDate = [NSDate date];
|
|
}
|
|
|
|
// clear dismissedDate
|
|
self.dismissedDate = nil;
|
|
|
|
if (self.step.earlyTerminationConfiguration != nil) {
|
|
self.skipButtonTitle = self.step.earlyTerminationConfiguration.buttonText;
|
|
}
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated {
|
|
[super viewWillDisappear:animated];
|
|
_dismissing = YES;
|
|
}
|
|
|
|
- (void)viewDidDisappear:(BOOL)animated {
|
|
|
|
[super viewDidDisappear:animated];
|
|
|
|
// Set endDate if the current ORKStepViewController's view disappears, and is not the topViewController anymore
|
|
// That way, if the step view controller is just covered by a modal view, dismissedDate will not be set.
|
|
if (self.nextResponder == nil ||
|
|
([self.parentViewController isKindOfClass:[UINavigationController class]]
|
|
&& ((UINavigationController *)self.parentViewController).topViewController != self)) {
|
|
self.dismissedDate = [NSDate date];
|
|
}
|
|
_dismissing = NO;
|
|
}
|
|
|
|
- (void)willNavigateDirection:(ORKStepViewControllerNavigationDirection)direction {
|
|
}
|
|
|
|
- (void)setContinueButtonTitle:(NSString *)continueButtonTitle {
|
|
_continueButtonTitle = continueButtonTitle;
|
|
self.internalContinueButtonItem.title = continueButtonTitle;
|
|
self.internalDoneButtonItem.title = continueButtonTitle;
|
|
|
|
self.continueButtonItem = self.internalContinueButtonItem;
|
|
}
|
|
|
|
- (void)showActivityIndicatorInContinueButton:(BOOL)showActivityIndicator {
|
|
[_navigationFooterView showActivityIndicator:showActivityIndicator];
|
|
}
|
|
|
|
- (void)setContinueButtonEnabled:(BOOL)continueButtonEnabled {
|
|
_navigationFooterView.continueEnabled = continueButtonEnabled;
|
|
}
|
|
|
|
- (void)setSkipButtonEnabled:(BOOL)skipButtonEnabled {
|
|
_navigationFooterView.skipEnabled = skipButtonEnabled;
|
|
}
|
|
|
|
- (void)setNavigationDetailText:(NSString *)navigationDetailText {
|
|
_navigationFooterView.navigationDetailText = navigationDetailText;
|
|
}
|
|
|
|
- (void)setContinueButtonDisabledStyle:(ORKBorderedButtonDisabledStyle)continueButtonDisabledStyle {
|
|
_continueButtonDisabledStyle = continueButtonDisabledStyle;
|
|
_navigationFooterView.continueButtonDisabledStyle = continueButtonDisabledStyle;
|
|
}
|
|
|
|
- (void)setLearnMoreButtonTitle:(NSString *)learnMoreButtonTitle {
|
|
self.learnMoreButtonItem.title = learnMoreButtonTitle;
|
|
self.learnMoreButtonItem = self.learnMoreButtonItem;
|
|
}
|
|
|
|
- (NSString *)learnMoreButtonTitle {
|
|
return self.learnMoreButtonItem.title;
|
|
}
|
|
|
|
- (void)setSkipButtonTitle:(NSString *)skipButtonTitle {
|
|
self.internalSkipButtonItem.title = skipButtonTitle;
|
|
self.skipButtonItem = self.internalSkipButtonItem;
|
|
}
|
|
|
|
- (NSString *)skipButtonTitle {
|
|
return self.skipButtonItem.title;
|
|
}
|
|
|
|
- (void)enableBackNavigation {
|
|
[self setBackButtonItem:[self goToPreviousPageButtonItem]];
|
|
}
|
|
|
|
- (UIBarButtonItem *)goToPreviousPageButtonItem {
|
|
UIBarButtonItem *button = [UIBarButtonItem ork_backBarButtonItemWithTarget:self action:@selector(goBackward)];
|
|
button.accessibilityLabel = ORKLocalizedString(@"AX_BUTTON_BACK", nil);
|
|
return button;
|
|
}
|
|
|
|
- (void)setBackButtonItem:(UIBarButtonItem *)backButton {
|
|
backButton.accessibilityLabel = ORKLocalizedString(@"AX_BUTTON_BACK", nil);
|
|
_backButtonItem = backButton;
|
|
[self updateBarButtonItems];
|
|
}
|
|
|
|
- (void)setCancelButtonItem:(UIBarButtonItem *)cancelButton {
|
|
_cancelButtonItem = cancelButton;
|
|
[self updateBarButtonItems];
|
|
}
|
|
|
|
- (BOOL)hasPreviousStep {
|
|
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
|
if (strongDelegate && [strongDelegate respondsToSelector:@selector(stepViewControllerHasPreviousStep:)]) {
|
|
return [strongDelegate stepViewControllerHasPreviousStep:self];
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)hasNextStep {
|
|
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
|
if (strongDelegate && [strongDelegate respondsToSelector:@selector(stepViewControllerHasNextStep:)]) {
|
|
return [strongDelegate stepViewControllerHasNextStep:self];
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (ORKStepResult *)result {
|
|
|
|
ORKStepResult *stepResult = [[ORKStepResult alloc] initWithStepIdentifier:self.step.identifier results:_addedResults ? : @[]];
|
|
stepResult.startDate = self.presentedDate ? : [NSDate date];
|
|
stepResult.endDate = self.dismissedDate ? : [NSDate date];
|
|
|
|
return stepResult;
|
|
}
|
|
|
|
- (void)addResult:(ORKResult *)result {
|
|
ORKResult *copy = [result copy];
|
|
if (_addedResults == nil) {
|
|
_addedResults = @[copy];
|
|
} else {
|
|
NSUInteger idx = [_addedResults indexOfObject:copy];
|
|
if (idx == NSNotFound) {
|
|
_addedResults = [_addedResults arrayByAddingObject:copy];
|
|
} else {
|
|
NSMutableArray *results = [_addedResults mutableCopy];
|
|
[results insertObject:copy atIndex:idx];
|
|
_addedResults = [results copy];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)notifyDelegateOnResultChange {
|
|
|
|
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
|
if ([strongDelegate respondsToSelector:@selector(stepViewControllerResultDidChange:)]) {
|
|
[strongDelegate stepViewControllerResultDidChange:self];
|
|
}
|
|
}
|
|
|
|
- (BOOL)hasBeenPresented {
|
|
return _hasBeenPresented;
|
|
}
|
|
|
|
+ (UIInterfaceOrientationMask)supportedInterfaceOrientations {
|
|
// The default values for a view controller's supported interface orientations is set to
|
|
// UIInterfaceOrientationMaskAll for the iPad idiom and UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.
|
|
UIInterfaceOrientationMask supportedOrientations = UIInterfaceOrientationMaskAllButUpsideDown;
|
|
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
|
|
supportedOrientations = UIInterfaceOrientationMaskAll;
|
|
}
|
|
return supportedOrientations;
|
|
}
|
|
|
|
- (BOOL)isBeingReviewed {
|
|
return _parentReviewStep != nil;
|
|
}
|
|
|
|
- (BOOL)readOnlyMode {
|
|
return self.isBeingReviewed && _parentReviewStep.isStandalone;
|
|
}
|
|
|
|
#pragma mark - Action Handlers
|
|
|
|
- (void)goForward {
|
|
_wasSkipped = false;
|
|
[self navigateForward];
|
|
}
|
|
|
|
- (void)navigateForward {
|
|
ORKStepViewControllerNavigationDirection direction = self.isBeingReviewed ? ORKStepViewControllerNavigationDirectionReverse : ORKStepViewControllerNavigationDirectionForward;
|
|
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
|
[strongDelegate stepViewController:self didFinishWithNavigationDirection:direction];
|
|
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
|
|
}
|
|
|
|
- (void)goBackward {
|
|
ORKStrongTypeOf(self.delegate) strongDelegate = self.delegate;
|
|
[strongDelegate stepViewController:self didFinishWithNavigationDirection:ORKStepViewControllerNavigationDirectionReverse];
|
|
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
|
|
}
|
|
|
|
- (void)skip:(UIView *)sender {
|
|
if (self.isBeingReviewed && !self.readOnlyMode) {
|
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil
|
|
message:nil
|
|
preferredStyle:UIAlertControllerStyleActionSheet];
|
|
[alert addAction:[UIAlertAction actionWithTitle:ORKLocalizedString(@"BUTTON_CLEAR_ANSWER", nil)
|
|
style:UIAlertActionStyleDestructive
|
|
handler:^(UIAlertAction *action) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self skipForward];
|
|
});
|
|
}]];
|
|
[alert addAction:[UIAlertAction actionWithTitle:ORKLocalizedString(@"BUTTON_CANCEL", nil)
|
|
style:UIAlertActionStyleCancel
|
|
handler:nil
|
|
]];
|
|
alert.popoverPresentationController.sourceView = sender;
|
|
alert.popoverPresentationController.sourceRect = sender.bounds;
|
|
[self presentViewController:alert animated:YES completion:nil];
|
|
} else {
|
|
[self skipForward];
|
|
}
|
|
}
|
|
|
|
- (void)skipForward {
|
|
_wasSkipped = true;
|
|
[self navigateForward];
|
|
}
|
|
|
|
- (UIView *)viewForiPadLayoutConstraints {
|
|
return _iPadContentView;
|
|
}
|
|
|
|
- (ORKTaskViewController *)taskViewController {
|
|
// look to parent view controller for a task view controller
|
|
UIViewController *parentViewController = [self parentViewController];
|
|
while (parentViewController && ![parentViewController isKindOfClass:[ORKTaskViewController class]]) {
|
|
parentViewController = [parentViewController parentViewController];
|
|
}
|
|
return (ORKTaskViewController *)parentViewController;
|
|
}
|
|
|
|
- (BOOL)showValidityAlertWithMessage:(NSString *)text {
|
|
return [self showValidityAlertWithTitle:ORKLocalizedString(@"RANGE_ALERT_TITLE", nil) message:text];
|
|
}
|
|
|
|
- (BOOL)showValidityAlertWithTitle:(NSString *)title message:(NSString *)message {
|
|
if (![title length] && ![message length]) {
|
|
// No alert if the value is empty
|
|
return NO;
|
|
}
|
|
if (_dismissing || ![self isViewLoaded] || !self.view.window) {
|
|
// No alert if not in view chain.
|
|
return NO;
|
|
}
|
|
|
|
if (_presentingAlert) {
|
|
return NO;
|
|
}
|
|
|
|
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
|
|
message:message
|
|
preferredStyle:UIAlertControllerStyleAlert];
|
|
|
|
[alert addAction:[UIAlertAction actionWithTitle:ORKLocalizedString(@"BUTTON_CANCEL", nil)
|
|
style:UIAlertActionStyleDefault
|
|
handler:nil]];
|
|
|
|
_presentingAlert = YES;
|
|
[self presentViewController:alert animated:YES completion:^{
|
|
_presentingAlert = NO;
|
|
}];
|
|
|
|
return YES;
|
|
}
|
|
|
|
|
|
#pragma mark - UIStateRestoring
|
|
|
|
static NSString *const _ORKStepIdentifierRestoreKey = @"stepIdentifier";
|
|
static NSString *const _ORKPresentedDateRestoreKey = @"presentedDate";
|
|
static NSString *const _ORKOutputDirectoryKey = @"outputDirectory";
|
|
static NSString *const _ORKParentReviewStepKey = @"parentReviewStep";
|
|
static NSString *const _ORKAddedResultsKey = @"addedResults";
|
|
|
|
- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
|
|
[super encodeRestorableStateWithCoder:coder];
|
|
|
|
[coder encodeObject:_step.identifier forKey:_ORKStepIdentifierRestoreKey];
|
|
[coder encodeObject:_presentedDate forKey:_ORKPresentedDateRestoreKey];
|
|
[coder encodeObject:ORKBookmarkDataFromURL(_outputDirectory) forKey:_ORKOutputDirectoryKey];
|
|
[coder encodeObject:_parentReviewStep forKey:_ORKParentReviewStepKey];
|
|
[coder encodeObject:_addedResults forKey:_ORKAddedResultsKey];
|
|
}
|
|
|
|
- (void)decodeRestorableStateWithCoder:(NSCoder *)coder {
|
|
[super decodeRestorableStateWithCoder:coder];
|
|
|
|
self.outputDirectory = ORKURLFromBookmarkData([coder decodeObjectOfClass:[NSData class] forKey:_ORKOutputDirectoryKey]);
|
|
|
|
if (!self.step) {
|
|
// Just logging to the console in this case, since this can happen during a taskVC restoration of a dynamic task.
|
|
// The step VC will get restored, but then never added back to the hierarchy.
|
|
ORK_Log_Debug("%@",[NSString stringWithFormat:@"No step provided while restoring %@", NSStringFromClass([self class])]);
|
|
}
|
|
|
|
self.presentedDate = [coder decodeObjectOfClass:[NSDate class] forKey:_ORKPresentedDateRestoreKey];
|
|
self.restoredStepIdentifier = [coder decodeObjectOfClass:[NSString class] forKey:_ORKStepIdentifierRestoreKey];
|
|
|
|
if (self.step && _restoredStepIdentifier && ![self.step.identifier isEqualToString:_restoredStepIdentifier]) {
|
|
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
|
reason:[NSString stringWithFormat:@"Attempted to restore step with identifier %@ but got step identifier %@", _restoredStepIdentifier, self.step.identifier]
|
|
userInfo:nil];
|
|
}
|
|
|
|
self.parentReviewStep = [coder decodeObjectOfClass:[ORKReviewStep class] forKey:_ORKParentReviewStepKey];
|
|
|
|
_addedResults = [coder decodeObjectOfClasses:[NSSet setWithArray:@[NSArray.self, ORKResult.self]] forKey:_ORKAddedResultsKey];
|
|
}
|
|
|
|
+ (UIViewController *)viewControllerWithRestorationIdentifierPath:(NSArray *)identifierComponents coder:(NSCoder *)coder {
|
|
ORKStepViewController *viewController = [[[self class] alloc] initWithStep:nil];
|
|
viewController.restorationIdentifier = identifierComponents.lastObject;
|
|
viewController.restorationClass = self;
|
|
return viewController;
|
|
}
|
|
|
|
#pragma mark - Accessibility
|
|
|
|
- (BOOL)accessibilityPerformEscape {
|
|
if ([self hasPreviousStep]) {
|
|
[self goBackward];
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
@end
|