From 667acf38317221af34832d63826fd92fc06d3936 Mon Sep 17 00:00:00 2001 From: Markus Emrich Date: Wed, 18 Oct 2023 21:44:44 +0200 Subject: [PATCH] add customView sizingController & use in presentSwiftView: --- .../project.pbxproj | 4 ++++ .../ExamplesScreen.swift | 1 + .../Private/JDSBNotificationView.h | 3 +++ .../Private/JDSBNotificationView.m | 12 ++++++++++ .../Public/JDStatusBarNotificationPresenter.h | 22 +++++++++++++++++++ .../Public/JDStatusBarNotificationPresenter.m | 11 ++++++++++ .../JDStatusBarNotificationPresenter.swift | 17 +++++++++++++- ...ationPresenterCustomViewSizingController.h | 17 ++++++++++++++ 8 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 JDStatusBarNotification/Public/JDStatusBarNotificationPresenterCustomViewSizingController.h diff --git a/ExampleProject/JDStatusBarNotificationExample.xcodeproj/project.pbxproj b/ExampleProject/JDStatusBarNotificationExample.xcodeproj/project.pbxproj index 4a556715..11fe555f 100644 --- a/ExampleProject/JDStatusBarNotificationExample.xcodeproj/project.pbxproj +++ b/ExampleProject/JDStatusBarNotificationExample.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 7E1C2838285226F5004315CC /* EnumPickerOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1C2836285226F5004315CC /* EnumPickerOptionView.swift */; }; 7E1C2839285226F5004315CC /* EnumPickerOptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E1C2836285226F5004315CC /* EnumPickerOptionView.swift */; }; 7E2A4E852AE70A4B001F0DB0 /* JDStatusBarNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB914CF2ADC11F4004B3435 /* JDStatusBarNotificationPresenter.swift */; }; + 7E2A4E8D2AE99D70001F0DB0 /* JDStatusBarNotificationPresenterCustomViewSizingController.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E2A4E8C2AE99D70001F0DB0 /* JDStatusBarNotificationPresenterCustomViewSizingController.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7E2F3BBB284F6144002B2181 /* ObservableCustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2F3BBA284F6144002B2181 /* ObservableCustomStyle.swift */; }; 7E2F3BBC284F6144002B2181 /* ObservableCustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2F3BBA284F6144002B2181 /* ObservableCustomStyle.swift */; }; 7E2F3BBD284F6144002B2181 /* ObservableCustomStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E2F3BBA284F6144002B2181 /* ObservableCustomStyle.swift */; }; @@ -110,6 +111,7 @@ 7E1C283128520F56004315CC /* JDSBNotificationAnimator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JDSBNotificationAnimator.h; sourceTree = ""; }; 7E1C283228520F56004315CC /* JDSBNotificationAnimator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JDSBNotificationAnimator.m; sourceTree = ""; }; 7E1C2836285226F5004315CC /* EnumPickerOptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumPickerOptionView.swift; sourceTree = ""; }; + 7E2A4E8C2AE99D70001F0DB0 /* JDStatusBarNotificationPresenterCustomViewSizingController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JDStatusBarNotificationPresenterCustomViewSizingController.h; sourceTree = ""; }; 7E2F3BBA284F6144002B2181 /* ObservableCustomStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableCustomStyle.swift; sourceTree = ""; }; 7E5402C2286708840079C579 /* JDStatusBarNotification.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JDStatusBarNotification.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7E5402C5286708850079C579 /* JDStatusBarNotification.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = JDStatusBarNotification.docc; sourceTree = ""; }; @@ -234,6 +236,7 @@ 7E8C519628585BE400C7C003 /* JDStatusBarNotificationPresenterPrepareStyleBlock.h */, 7E8C519328585BE400C7C003 /* JDStatusBarNotificationStyle.h */, 7E8C519728585BE400C7C003 /* JDStatusBarNotificationStyle.m */, + 7E2A4E8C2AE99D70001F0DB0 /* JDStatusBarNotificationPresenterCustomViewSizingController.h */, ); path = Public; sourceTree = ""; @@ -337,6 +340,7 @@ 7E5402E5286709560079C579 /* JDStatusBarNotificationPresenter.h in Headers */, 7E5402E72867095C0079C579 /* JDStatusBarNotificationPresenterPrepareStyleBlock.h in Headers */, 7E5402E6286709590079C579 /* JDStatusBarNotificationStyle.h in Headers */, + 7E2A4E8D2AE99D70001F0DB0 /* JDStatusBarNotificationPresenterCustomViewSizingController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ExampleProject/JDStatusBarNotificationExample/ExamplesScreen.swift b/ExampleProject/JDStatusBarNotificationExample/ExamplesScreen.swift index 8825af8b..1e659d49 100644 --- a/ExampleProject/JDStatusBarNotificationExample/ExamplesScreen.swift +++ b/ExampleProject/JDStatusBarNotificationExample/ExamplesScreen.swift @@ -239,6 +239,7 @@ struct ExamplesScreen: View { } .padding(6.0) .padding([.leading, .trailing], 10.0) + .fixedSize() } NotificationPresenter.shared().dismiss(afterDelay: 2.5) diff --git a/JDStatusBarNotification/Private/JDSBNotificationView.h b/JDStatusBarNotification/Private/JDSBNotificationView.h index 637996e0..85f1ef09 100644 --- a/JDStatusBarNotification/Private/JDSBNotificationView.h +++ b/JDStatusBarNotification/Private/JDSBNotificationView.h @@ -7,6 +7,8 @@ #import +#import + NS_ASSUME_NONNULL_BEGIN @class JDStatusBarNotificationStyle; @@ -35,6 +37,7 @@ NS_SWIFT_NAME(_SBNotificationView) /// state like rotation, status bar visibility, etc..) It never overlaps the statusbar. The height & width is defined /// by the selected style. In the pill style the view will match the pill size. @property (nonatomic, strong, nullable) UIView *customSubview; +@property (nonatomic, strong, nullable) id customSubviewSizingController; /// A left view will be displayed left of the text. The layout can be adjusted using the leftViewStyle. @property (nonatomic, strong, nullable) UIView *leftView; diff --git a/JDStatusBarNotification/Private/JDSBNotificationView.m b/JDStatusBarNotification/Private/JDSBNotificationView.m index f1674ed0..024cd076 100644 --- a/JDStatusBarNotification/Private/JDSBNotificationView.m +++ b/JDStatusBarNotification/Private/JDSBNotificationView.m @@ -93,6 +93,7 @@ - (void)resetSubviews { // reset custom subview _customSubview = nil; + _customSubviewSizingController = nil; if (_leftView != _activityIndicatorView) { _leftView = nil; } @@ -278,6 +279,11 @@ - (void)setCustomSubview:(UIView *)customSubview { [self setNeedsLayout]; } +- (void)setCustomSubviewSizingController:(id)customSubviewSizingController { + _customSubviewSizingController = customSubviewSizingController; + [self setNeedsLayout]; +} + #pragma mark - Style - (void)setStyle:(JDStatusBarNotificationStyle *)style { @@ -386,6 +392,12 @@ - (CGRect)pillContentRectForContentRect:(CGRect)contentRect { // layout pill CGFloat maxTextWidth = MAX(realTextSizeForLabel(_titleLabel).width, realTextSizeForLabel(_subtitleLabel).width); + if (_customSubview) { + const CGSize sizeThatFits = (_customSubviewSizingController + ? [_customSubviewSizingController sizeThatFits:contentRect.size] + : [_customSubview sizeThatFits:contentRect.size]); + maxTextWidth = MAX(maxTextWidth, sizeThatFits.width); + } CGFloat pillWidth = round(MAX(minimumPillWidth, MIN(maximumPillWidth, maxTextWidth + paddingX * 2))); CGFloat pillX = round(MAX(minimumPillInset, (CGRectGetWidth(self.bounds) - pillWidth)/2.0)); CGFloat pillY = round(contentRect.origin.y + contentRect.size.height - pillHeight); diff --git a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.h b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.h index 4612cf4d..df4b0627 100644 --- a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.h +++ b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.h @@ -11,6 +11,7 @@ #import #import +#import @class JDStatusBarNotificationPresenter; @@ -242,6 +243,27 @@ NS_SWIFT_NAME(NotificationPresenter) styleName:(NSString * _Nullable)styleName completion:(JDStatusBarNotificationPresenterCompletionBlock)completion NS_SWIFT_NAME(present(customView:style:completion:)); +/// Present a notification using a custom subview. +/// +/// The `customView` will be layouted correctly according to the selected style & the current device +/// state (rotation, status bar visibility, etc.). The background will still be styled & layouted +/// according to the provided style. If your custom view requires custom touch handling, +/// make sure to set `style.canTapToHold` to `false`. Otherwise the `customView` won't +/// receive any touches, as the internal `gestureRecognizer` would receive them. +/// +/// - Parameters: +/// - customView: A custom UIView to display as notification content. +/// - styleName: The name of the style. You can use styles previously added using e.g. ``addStyleNamed:prepare:``. +/// If no style can be found for the given `styleName` or it is `nil`, the default style will be used. +/// - completion: A ``JDStatusBarNotificationPresenterCompletionBlock``, which gets called once the presentation animation finishes. +/// +/// - Returns: The presented UIView for further customization +/// +- (UIView *)presentWithCustomView:(UIView *)customView + sizingController:(id _Nullable)sizingController + styleName:(NSString * _Nullable)styleName + completion:(JDStatusBarNotificationPresenterCompletionBlock)completion NS_SWIFT_NAME(present(customView:sizingController:style:completion:)); + #pragma mark - Dismissal /// Dismisses any currently displayed notification immediately using an animation. diff --git a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.m b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.m index e63beda6..8a0a2449 100644 --- a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.m +++ b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.m @@ -178,9 +178,20 @@ - (UIView *)presentWithTitle:(NSString *)title - (UIView *)presentWithCustomView:(UIView *)customView styleName:(NSString * _Nullable)styleName completion:(JDStatusBarNotificationPresenterCompletionBlock)completion { + return [self presentWithCustomView:customView + sizingController:nil + styleName:styleName + completion:completion]; +} + +- (UIView *)presentWithCustomView:(UIView *)customView + sizingController:(id _Nullable)sizingController + styleName:(NSString * _Nullable)styleName + completion:(JDStatusBarNotificationPresenterCompletionBlock)completion { JDStatusBarNotificationStyle *style = [_styleCache styleForName:styleName]; JDSBNotificationView *view = [self presentWithTitle:nil subtitle:nil style:style completion:completion]; view.customSubview = customView; + view.customSubviewSizingController = sizingController; return view; } diff --git a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.swift b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.swift index bb077bab..b3637942 100644 --- a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.swift +++ b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenter.swift @@ -7,6 +7,18 @@ import SwiftUI +class NotificationPresenterSizingController: NotificationPresenterCustomViewSizingController where Content: View { + let hostingController: UIHostingController + + init(hostingController: UIHostingController) { + self.hostingController = hostingController + } + + func sizeThatFits(in size: CGSize) -> CGSize { + return hostingController.sizeThatFits(in: size) + } +} + extension NotificationPresenter { public func presentSwiftView(style: String? = nil, @@ -14,7 +26,10 @@ extension NotificationPresenter { completion: NotificationPresenterCompletion? = nil) { let controller = UIHostingController(rootView: viewBuilder()) controller.view.backgroundColor = .clear - self.present(customView: controller.view, style: style, completion: completion) + self.present(customView: controller.view, + sizingController: NotificationPresenterSizingController(hostingController: controller), + style: style, + completion: completion) } } diff --git a/JDStatusBarNotification/Public/JDStatusBarNotificationPresenterCustomViewSizingController.h b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenterCustomViewSizingController.h new file mode 100644 index 00000000..9a013daa --- /dev/null +++ b/JDStatusBarNotification/Public/JDStatusBarNotificationPresenterCustomViewSizingController.h @@ -0,0 +1,17 @@ +// +// JDStatusBarNotificationPresenterCustomViewSizingController.h +// +// Created by Markus Emrich on 10/25/23. +// Copyright 2023 Markus Emrich. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(NotificationPresenterCustomViewSizingController) +@protocol JDStatusBarNotificationPresenterCustomViewSizingController +- (CGSize)sizeThatFits:(CGSize)size NS_SWIFT_NAME(sizeThatFits(in:)); +@end + +NS_ASSUME_NONNULL_END