Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ permissions:
contents: read
jobs:
build:
runs-on: macos-26
runs-on: xcode-27
env:
# https://github.com/actions/runner-images/blob/main/images/macos/macos-26-arm64-Readme.md
xcode: '/Applications/Xcode_26.2.app'
destination_ios: 'platform=iOS Simulator,OS=26.2,name=iPhone 17'
# https://github.com/actions/runner-images/blob/main/images/macos/xcode-27-arm64-Readme.md
xcode: '/Applications/Xcode_27.0.app'
destination_ios: 'platform=iOS Simulator,OS=27.0,name=iPhone 17'
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
token: ${{ secrets.CAREEVOLUTIONBUILD_TOKEN }}
submodules: 'recursive'
Expand Down
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "CareEvolution/MarkdownAttributedString" "1.0.4"
github "CareEvolution/MarkdownAttributedString" "1.0.5"
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
LastUpgradeVersion = "2700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
LastUpgradeVersion = "2700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
4 changes: 3 additions & 1 deletion ORK1Kit/ORK1Kit/Common/CEVRK1NavigationBarProgressView.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

@interface CEVRK1NavigationBarProgressView : UIView

- (void)setProgress:(float)progress withTheme:(nullable CEVRK1Theme *)theme;
@property (nonatomic, readonly) float progress;

- (void)setProgress:(float)progress withTheme:(nullable CEVRK1Theme *)theme animated:(BOOL)animated;

@end
36 changes: 25 additions & 11 deletions ORK1Kit/ORK1Kit/Common/CEVRK1NavigationBarProgressView.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,33 @@ - (void)createConstraints {
[self addSubview:_progressView];
_progressView.translatesAutoresizingMaskIntoConstraints = NO;

// This forces the bar to stretch so the ORK1ProgressView will attempt to take up the entire available width
NSLayoutConstraint *widthConstraint = [self.widthAnchor constraintEqualToConstant:200];
widthConstraint.priority = UILayoutPriorityDefaultHigh;
[NSLayoutConstraint activateConstraints:@[
[_progressView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10],
[_progressView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-30],
[_progressView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
widthConstraint
]];
if (@available(iOS 26.0, *)) {
_progressView.backgroundColor = [UIColor whiteColor];
[NSLayoutConstraint activateConstraints:@[

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are no vertical constraints on the progressView here leaving placement ambiguous. I think this could present problems. Here are a few edge cases I saw in the CEVRKUITestVehicle. One option is we could give the container a real constant height first, then pin the progress bar to the bottom. I'm not opposed to the current solution, just considering it may pose aesthetic problems in existing surveys without much spacing to top.

Image|Image

[_progressView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[_progressView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[_progressView.topAnchor constraintEqualToAnchor:self.topAnchor],
[_progressView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
]];
} else {
// This forces the bar to stretch so the ORK1ProgressView will attempt to take up the entire available width
NSLayoutConstraint *widthConstraint = [self.widthAnchor constraintEqualToConstant:200];
widthConstraint.priority = UILayoutPriorityDefaultHigh;
[NSLayoutConstraint activateConstraints:@[
[_progressView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:10],
[_progressView.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-30],
[_progressView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
widthConstraint
]];
}
}

- (float)progress {
return _progressView.progress;
}

- (void)setProgress:(float)progress withTheme:(nullable CEVRK1Theme *)theme {
_progressView.progress = progress;
- (void)setProgress:(float)progress withTheme:(nullable CEVRK1Theme *)theme animated:(BOOL)animated {
[_progressView setProgress:progress animated:animated];
Comment thread
mmertsock marked this conversation as resolved.
if (theme.progressBarColor) {
_progressView.tintColor = theme.progressBarColor;
}
Expand Down
20 changes: 16 additions & 4 deletions ORK1Kit/ORK1Kit/Common/ORK1ImageCaptureStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,22 @@ - (instancetype)initWithStep:(ORK1Step *)step {
- (void)setUpConstraints {
NSMutableArray *constraints = [NSMutableArray new];
NSDictionary *views = @{ @"imageCaptureView": _imageCaptureView };
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageCaptureView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:views]];
// Top edge: on iOS 26+, pin to the safe area rather than the raw view edge, so this full-bleed
// view still reserves space for CEVRK1's navigation-bar progress view (added via
// additionalSafeAreaInsets in ORK1TaskViewController's configureProgressView) instead of
// rendering underneath it. Leading/trailing/bottom stay pinned to the raw edges (unchanged,
// still full-bleed there) — the nav bar is already opaque on iOS 26+ regardless of progress
// style (see ORK1TaskViewController commonInit), so this is a no-op except when the extra
// progress-bar inset is actually present. iOS <26 is left on the original raw-edge pin.
if (@available(iOS 26.0, *)) {
[constraints addObject:[_imageCaptureView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor]];
} else {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageCaptureView]"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:views]];
}
[constraints addObject:[_imageCaptureView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[imageCaptureView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
Expand Down
72 changes: 64 additions & 8 deletions ORK1Kit/ORK1Kit/Common/ORK1TaskViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,15 @@ - (instancetype)commonInitWithTask:(id<ORK1Task>)task taskRunUUID:(NSUUID *)task

self.taskRunUUID = taskRunUUID;

[self.childNavigationController.navigationBar setShadowImage:[UIImage new]];
self.hairline = [self findHairlineViewUnder:self.childNavigationController.navigationBar];
self.hairline.alpha = 0.0f;
self.childNavigationController.toolbar.clipsToBounds = YES;

if (@available(iOS 26.0, *)) {
// ResearchKit views render poorly with translucent navigation bars on iOS 26
self.childNavigationController.navigationBar.backgroundColor = [UIColor whiteColor];
}

// Ensure taskRunUUID has non-nil valuetaskRunUUID
(void)[self taskRunUUID];
self.restorationClass = [ORK1TaskViewController class];
Expand Down Expand Up @@ -1027,6 +1031,11 @@ - (void)showViewController:(ORK1StepViewController *)viewController goForward:(B
// remove any progress
strongSelf.pageViewController.navigationItem.titleView = nil;
strongSelf.pageViewController.navigationItem.title = nil;
strongSelf.progressView.hidden = YES;
if (@available(iOS 26.0, *)) {
// Reset safe area insets that are adjusted when progress view is visible.
strongSelf.pageViewController.additionalSafeAreaInsets = UIEdgeInsetsZero;
}
} else {
ORK1OrderedTask *orderedTask = (ORK1OrderedTask *)strongSelf.task;
if (orderedTask.progressIndicatorStyle == CEVRK1TaskProgressIndicatorStyleBar) {
Expand All @@ -1037,16 +1046,17 @@ - (void)showViewController:(ORK1StepViewController *)viewController goForward:(B
} else { // Linear
calculatedProgress = (float)taskProgress.current / (float)taskProgress.total;
}
[strongSelf.progressView setProgress:calculatedProgress withTheme:[CEVRK1Theme themeForElement:strongSelf.currentStepViewController]];
strongSelf.pageViewController.navigationItem.titleView = strongSelf.progressView;

// for UITesting, we will add a title that will not display, but should appear via accessibility
NSUInteger progressPercent = (NSUInteger)(calculatedProgress * 100);
strongSelf.pageViewController.navigationItem.title = [NSString stringWithFormat:@"ProgressBar:%@", @(progressPercent)];

[strongSelf.progressView setProgress:calculatedProgress withTheme:[CEVRK1Theme themeForElement:strongSelf.currentStepViewController] animated:animated];
[strongSelf configureProgressView];
strongSelf.progressView.hidden = NO;
} else {
strongSelf.pageViewController.navigationItem.titleView = nil;
strongSelf.pageViewController.navigationItem.title = [NSString localizedStringWithFormat:ORK1LocalizedString(@"STEP_PROGRESS_FORMAT", nil) ,ORK1LocalizedStringFromNumber(@(taskProgress.current)), ORK1LocalizedStringFromNumber(@(taskProgress.total))];
strongSelf.progressView.hidden = YES;
if (@available(iOS 26.0, *)) {
// Reset safe area insets that are adjusted when progress view is visible.
strongSelf.pageViewController.additionalSafeAreaInsets = UIEdgeInsetsZero;
}
}
}
}
Expand All @@ -1058,6 +1068,52 @@ - (void)showViewController:(ORK1StepViewController *)viewController goForward:(B
}];
}

- (void)configureProgressView {
if (@available(iOS 26.0, *)) {
self.pageViewController.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeNever;
self.pageViewController.navigationItem.titleView = [UIView new];

#if DEBUG
// for UITesting, we will add a title that will not display (suppressed by the empty
// titleView above), but should appear via accessibility
NSUInteger progressPercent = (NSUInteger)(self.progressView.progress * 100);
self.pageViewController.navigationItem.title = [NSString stringWithFormat:@"ProgressBar:%@", @(progressPercent)];
#else
self.pageViewController.navigationItem.title = nil;
#endif

CEVRK1NavigationBarProgressView *progressView = self.progressView;
UINavigationBar *navigationBar = self.childNavigationController.navigationBar;
if (!progressView.superview) {
navigationBar.opaque = YES;
navigationBar.translucent = NO;

progressView.translatesAutoresizingMaskIntoConstraints = NO;
[navigationBar addSubview:progressView];
[NSLayoutConstraint activateConstraints:@[
[progressView.topAnchor constraintEqualToAnchor:navigationBar.bottomAnchor],
[progressView.leadingAnchor constraintEqualToAnchor:navigationBar.safeAreaLayoutGuide.leadingAnchor constant:10],
[progressView.trailingAnchor constraintEqualToAnchor:navigationBar.safeAreaLayoutGuide.trailingAnchor constant:-10]
]];
}

// Scoped to navigationBar rather than self.view: progressView's constraints are entirely
// self-contained within the nav bar subtree, and self.view's subtree also contains whatever
// step content view is currently presented. Forcing that to lay out early (before it's
// necessarily attached to a window, mid-transition) can trip pre-existing updateConstraints
// bugs in step views (e.g. ORK1ImageCaptureView) that assume they're only laid out once
// installed in a window.
[navigationBar layoutIfNeeded];
self.pageViewController.additionalSafeAreaInsets = UIEdgeInsetsMake(progressView.bounds.size.height, 0, 0, 0);
} else {
self.pageViewController.navigationItem.titleView = self.progressView;

// for UITesting, we will add a title that will not display, but should appear via accessibility
NSUInteger progressPercent = (NSUInteger)(self.progressView.progress * 100);
self.pageViewController.navigationItem.title = [NSString stringWithFormat:@"ProgressBar:%@", @(progressPercent)];
}
}

- (BOOL)shouldPresentStep:(ORK1Step *)step {
BOOL shouldPresent = (step != nil);

Expand Down
20 changes: 16 additions & 4 deletions ORK1Kit/ORK1Kit/Common/ORK1VideoCaptureStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,22 @@ - (void)setUpConstraints {
NSDictionary *views = @{ @"videoCaptureView": _videoCaptureView };
_videoCaptureView.translatesAutoresizingMaskIntoConstraints = NO;

[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[videoCaptureView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:views]];
// Top edge: on iOS 26+, pin to the safe area rather than the raw view edge, so this full-bleed
// view still reserves space for CEVRK1's navigation-bar progress view (added via
// additionalSafeAreaInsets in ORK1TaskViewController's configureProgressView) instead of
// rendering underneath it. Leading/trailing/bottom stay pinned to the raw edges (unchanged,
// still full-bleed there) — the nav bar is already opaque on iOS 26+ regardless of progress
// style (see ORK1TaskViewController commonInit), so this is a no-op except when the extra
// progress-bar inset is actually present. iOS <26 is left on the original raw-edge pin.
if (@available(iOS 26.0, *)) {
[constraints addObject:[_videoCaptureView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor]];
} else {
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[videoCaptureView]"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:views]];
}
[constraints addObject:[_videoCaptureView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[videoCaptureView]|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
Expand Down
22 changes: 20 additions & 2 deletions ORK1Kit/ORK1Kit/Common/ORK1WebViewStepViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,27 @@ - (ORK1StepResult *)result {

- (void)viewDidLoad {
[super viewDidLoad];
_webView.frame = self.view.bounds;
_webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:_webView];
_webView.translatesAutoresizingMaskIntoConstraints = NO;

// Top edge: on iOS 26+, pin to the safe area rather than the raw view edge, so this full-bleed
// view still reserves space for CEVRK1's navigation-bar progress view (added via
// additionalSafeAreaInsets in ORK1TaskViewController's configureProgressView) instead of
// rendering underneath it. Leading/trailing/bottom stay pinned to the raw edges (unchanged,
// still full-bleed there) — the nav bar is already opaque on iOS 26+ regardless of progress
// style (see ORK1TaskViewController commonInit), so this is a no-op except when the extra
// progress-bar inset is actually present. iOS <26 is left on the original raw-edge pin
// (previously achieved via frame = self.view.bounds; now via an equivalent top anchor).
NSLayoutYAxisAnchor *topAnchor = self.view.topAnchor;
if (@available(iOS 26.0, *)) {
topAnchor = self.view.safeAreaLayoutGuide.topAnchor;
}
[NSLayoutConstraint activateConstraints:@[
[_webView.topAnchor constraintEqualToAnchor:topAnchor],
[_webView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[_webView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[_webView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
]];
}

- (void)viewDidDisappear:(BOOL)animated {
Expand Down
14 changes: 2 additions & 12 deletions ORK1Kit/ORK1Kit/Common/UIBarButtonItem+ORK1BarButtonItem.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,9 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
@implementation UIBarButtonItem (ORK1BarButtonItem)

+ (UIBarButtonItem *)ork_backBarButtonItemWithTarget:(id)target action:(SEL)selector {
NSString *regularImageName = @"arrowLeft";
NSString *landscapeImageName = @"arrowLeftLandscape";

if ([UIApplication sharedApplication].userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft ) {
regularImageName = @"arrowRight";
landscapeImageName = @"arrowRightLandscape";
}

UIImage *image = [UIImage imageNamed:regularImageName inBundle:ORK1Bundle() compatibleWithTraitCollection:nil];
UIImage *landscapeImage = [UIImage imageNamed:landscapeImageName inBundle:ORK1Bundle() compatibleWithTraitCollection:nil];
UIImage *image = [UIImage systemImageNamed:@"chevron.backward"];
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithImage:image
landscapeImagePhone:landscapeImage
style:UIBarButtonItemStyleDone
style:UIBarButtonItemStylePlain
target:target
action:selector];
item.accessibilityLabel = ORK1LocalizedString(@"AX_BUTTON_BACK", nil);
Expand Down
2 changes: 1 addition & 1 deletion ORK1Kit/ORK1Kit/Consent/ORK1ConsentReviewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ - (instancetype)initWithHTML:(NSString *)html delegate:(id<ORK1ConsentReviewCont
_delegate = delegate;
_webViewFinishedLoading = NO;

_agreeButton = [[UIBarButtonItem alloc] initWithTitle:ORK1LocalizedString(@"BUTTON_AGREE", nil) style:UIBarButtonItemStylePlain target:self action:@selector(ack)];
_agreeButton = [[UIBarButtonItem alloc] initWithTitle:ORK1LocalizedString(@"BUTTON_AGREE", nil) style:UIBarButtonItemStyleDone target:self action:@selector(ack)];
if (requiresScrollToBottom) {
_agreeButton.enabled = NO;
_agreeButton.accessibilityHint = @"must scroll to the bottom to enable this button";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
LastUpgradeVersion = "2700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
2 changes: 1 addition & 1 deletion ResearchKit.xcodeproj/xcshareddata/xcschemes/docs.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "2620"
LastUpgradeVersion = "2700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Loading