diff --git a/BrazeKit.podspec b/BrazeKit.podspec index 5620c93a10..e38f730899 100644 --- a/BrazeKit.podspec +++ b/BrazeKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeKit' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Braze Main SDK library providing support for analytics and push notifications.' s.homepage = 'https://braze.com' @@ -9,8 +9,8 @@ Pod::Spec.new do |s| s.authors = 'Braze, Inc.' s.source = { - :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeKit.zip', - :sha256 => '4a1e89795467329b8e30f069d9df34acd4a95d179527c41dffe2f1d336533728' + :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeKit.zip', + :sha256 => 'ab2660fa83c6aba270d205dae8a298be642059cd64e1a0bbfa8a1a06fe02107d' } s.swift_version = '5.0' diff --git a/BrazeKitCompat.podspec b/BrazeKitCompat.podspec index 2b08f5dc5d..742c2fa97f 100644 --- a/BrazeKitCompat.podspec +++ b/BrazeKitCompat.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeKitCompat' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Compatibility library for users migrating from AppboyKit.' s.homepage = 'https://braze.com' @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.license = { :type => 'Commercial' } s.authors = 'Braze, Inc.' - s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.9.1' } + s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.10.0' } s.swift_version = '5.0' s.ios.deployment_target = '11.0' @@ -18,8 +18,8 @@ Pod::Spec.new do |s| s.source_files = 'Sources/BrazeKitCompat/**/*.{h,m}' s.public_header_files = 'Sources/BrazeKitCompat/include/*.h' - s.dependency 'BrazeKit', '5.9.1' - s.dependency 'BrazeLocation', '5.9.1' + s.dependency 'BrazeKit', '5.10.0' + s.dependency 'BrazeLocation', '5.10.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end diff --git a/BrazeLocation.podspec b/BrazeLocation.podspec index d67869857a..aba67bcb5c 100644 --- a/BrazeLocation.podspec +++ b/BrazeLocation.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeLocation' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Braze location library providing support for location analytics and geofence monitoring.' s.homepage = 'https://braze.com' @@ -9,8 +9,8 @@ Pod::Spec.new do |s| s.authors = 'Braze, Inc.' s.source = { - :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeLocation.zip', - :sha256 => 'f2b52b2db62cc3d2675a3b831692cba482d20d3b7d3002a335b15266e7ae1cc9' + :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeLocation.zip', + :sha256 => '7e0ff737566c98b9293ea3f9dec6d3589c70822f6caf98c21f361585cc83a202' } s.swift_version = '5.0' @@ -21,7 +21,7 @@ Pod::Spec.new do |s| # Depends on BrazeKit because BrazeKit includes the internal _BrazeLocationClient symbols required # for linking against BrazeLocation. - s.dependency 'BrazeKit', '5.9.1' + s.dependency 'BrazeKit', '5.10.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end diff --git a/BrazeNotificationService.podspec b/BrazeNotificationService.podspec index 12a6ffb801..d18a02d35f 100644 --- a/BrazeNotificationService.podspec +++ b/BrazeNotificationService.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeNotificationService' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Braze notification service extension library providing support for Rich Push notifications.' s.homepage = 'https://braze.com' @@ -9,8 +9,8 @@ Pod::Spec.new do |s| s.authors = 'Braze, Inc.' s.source = { - :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeNotificationService.zip', - :sha256 => '5227b1321b27505a106809e71f60390671f35d292fb1accf29c2f1869c7b28af' + :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeNotificationService.zip', + :sha256 => 'a068c73678861acdf6378cad866ab1a53372fbdc3c70d0780d54f96f0baff8f2' } s.swift_version = '5.0' diff --git a/BrazePushStory.podspec b/BrazePushStory.podspec index 05f4afd2bf..df8d3e9afa 100644 --- a/BrazePushStory.podspec +++ b/BrazePushStory.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazePushStory' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Braze notification content extension library providing support for Push Stories.' s.homepage = 'https://braze.com' @@ -9,8 +9,8 @@ Pod::Spec.new do |s| s.authors = 'Braze, Inc.' s.source = { - :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazePushStory.zip', - :sha256 => 'af2a93ca9a00be0d3840f91290bb7cdf1fb75798d7b7017b881b2f7606a6824d' + :http => 'https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazePushStory.zip', + :sha256 => '81e13d3247a5ad6af899a5e77336ebeee759735c188caad2f52c40e33f4ddc54' } s.swift_version = '5.0' diff --git a/BrazeUI.podspec b/BrazeUI.podspec index 720fb6c153..b68f315d3b 100644 --- a/BrazeUI.podspec +++ b/BrazeUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeUI' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Braze-provided user interface library for In-App Messages and Content Cards.' s.homepage = 'https://braze.com' @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.license = { :type => 'Commercial' } s.authors = 'Braze, Inc.' - s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.9.1' } + s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.10.0' } s.swift_version = '5.0' s.ios.deployment_target = '11.0' @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.source_files = 'Sources/BrazeUI/**/*.swift' s.resource_bundles = { 'BrazeUI' => ['Sources/BrazeUI/Resources/**/*'] } - s.dependency 'BrazeKit', '5.9.1' + s.dependency 'BrazeKit', '5.10.0' s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } end diff --git a/BrazeUICompat.podspec b/BrazeUICompat.podspec index ba337afcb0..61a8a52066 100644 --- a/BrazeUICompat.podspec +++ b/BrazeUICompat.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'BrazeUICompat' - s.version = '5.9.1' + s.version = '5.10.0' s.summary = 'Compatibility UI library for users migrating from AppboyUI.' s.homepage = 'https://braze.com' @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.license = { :type => 'Commercial' } s.authors = 'Braze, Inc.' - s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.9.1' } + s.source = { :git => 'https://github.com/braze-inc/braze-swift-sdk.git', :tag => '5.10.0' } s.swift_version = '5.0' s.ios.deployment_target = '11.0' @@ -18,7 +18,7 @@ Pod::Spec.new do |s| s.public_header_files = 'Sources/BrazeUICompat/ABK*/**/*.h' s.resource_bundles = { 'BrazeUICompat' => 'Sources/BrazeUICompat/*/Resources/**/*.*' } - s.dependency 'BrazeKitCompat', '5.9.1' + s.dependency 'BrazeKitCompat', '5.10.0' s.dependency 'SDWebImage', '>= 5.8.2', '< 6' s.user_target_xcconfig = { 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES' } diff --git a/CHANGELOG.md b/CHANGELOG.md index 0762334b3f..7807538642 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## 5.10.0 + +##### Fixed +- Fixes an issue where test content cards were removed before their expiration date. +- Fixes an issue in `BrazeUICompat` where the status bar appearance wasn't restored to its original state after dismissing a full in-app message. +- Fixes an issue when decoding notification payloads where some valid boolean values weren't correctly parsed. + +##### Changed +- In-app modal and full-screen messages are now rendered with `UITextView`, which better supports large amounts of text and extended UTF code points. + ## 5.9.1 ##### Fixed diff --git a/Examples/ObjC/Sources/ContentCards/Info.plist b/Examples/ObjC/Sources/ContentCards/Info.plist deleted file mode 100644 index 0c015f1468..0000000000 --- a/Examples/ObjC/Sources/ContentCards/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ContentCards - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UILaunchStoryboardName - LaunchScreen - - diff --git a/Examples/ObjC/Sources/InAppMessages/Info.plist b/Examples/ObjC/Sources/InAppMessages/Info.plist deleted file mode 100644 index 4f97542122..0000000000 --- a/Examples/ObjC/Sources/InAppMessages/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - InAppMessages - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UILaunchStoryboardName - LaunchScreen - - diff --git a/Examples/ObjC/manual-integration-setup.sh b/Examples/ObjC/manual-integration-setup.sh index d676523bd6..c75f2a1beb 100755 --- a/Examples/ObjC/manual-integration-setup.sh +++ b/Examples/ObjC/manual-integration-setup.sh @@ -20,7 +20,7 @@ if [ ! -f "manual-integration-setup.sh" ]; then fi # Constants -url="https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/braze-swift-sdk-prebuilt.zip" +url="https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/braze-swift-sdk-prebuilt.zip" echo "→" "Cleaning up" rm -rf braze-swift-sdk-prebuilt diff --git a/Examples/Swift/Sources/ContentCards/Info.plist b/Examples/Swift/Sources/ContentCards/Info.plist deleted file mode 100644 index 0c015f1468..0000000000 --- a/Examples/Swift/Sources/ContentCards/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - ContentCards - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UILaunchStoryboardName - LaunchScreen - - diff --git a/Examples/Swift/Sources/InAppMessages/Info.plist b/Examples/Swift/Sources/InAppMessages/Info.plist deleted file mode 100644 index 4f97542122..0000000000 --- a/Examples/Swift/Sources/InAppMessages/Info.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - InAppMessages - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - UILaunchStoryboardName - LaunchScreen - - diff --git a/Examples/Swift/manual-integration-setup.sh b/Examples/Swift/manual-integration-setup.sh index d676523bd6..c75f2a1beb 100755 --- a/Examples/Swift/manual-integration-setup.sh +++ b/Examples/Swift/manual-integration-setup.sh @@ -20,7 +20,7 @@ if [ ! -f "manual-integration-setup.sh" ]; then fi # Constants -url="https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/braze-swift-sdk-prebuilt.zip" +url="https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/braze-swift-sdk-prebuilt.zip" echo "→" "Cleaning up" rm -rf braze-swift-sdk-prebuilt diff --git a/Package.swift b/Package.swift index 704ec2af96..2d24daeae7 100644 --- a/Package.swift +++ b/Package.swift @@ -47,8 +47,8 @@ let package = Package( targets: [ .binaryTarget( name: "BrazeKit", - url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeKit.zip", - checksum: "4a1e89795467329b8e30f069d9df34acd4a95d179527c41dffe2f1d336533728" + url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeKit.zip", + checksum: "ab2660fa83c6aba270d205dae8a298be642059cd64e1a0bbfa8a1a06fe02107d" ), .target( name: "BrazeKitResources", @@ -65,18 +65,18 @@ let package = Package( ), .binaryTarget( name: "BrazeLocation", - url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeLocation.zip", - checksum: "f2b52b2db62cc3d2675a3b831692cba482d20d3b7d3002a335b15266e7ae1cc9" + url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeLocation.zip", + checksum: "7e0ff737566c98b9293ea3f9dec6d3589c70822f6caf98c21f361585cc83a202" ), .binaryTarget( name: "BrazeNotificationService", - url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazeNotificationService.zip", - checksum: "5227b1321b27505a106809e71f60390671f35d292fb1accf29c2f1869c7b28af" + url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazeNotificationService.zip", + checksum: "a068c73678861acdf6378cad866ab1a53372fbdc3c70d0780d54f96f0baff8f2" ), .binaryTarget( name: "BrazePushStory", - url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.9.1/BrazePushStory.zip", - checksum: "af2a93ca9a00be0d3840f91290bb7cdf1fb75798d7b7017b881b2f7606a6824d" + url: "https://github.com/braze-inc/braze-swift-sdk/releases/download/5.10.0/BrazePushStory.zip", + checksum: "81e13d3247a5ad6af899a5e77336ebeee759735c188caad2f52c40e33f4ddc54" ), .target( name: "BrazeKitCompat", diff --git a/README.md b/README.md index bcfa899286..199a0414e6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

- Version: 5.9.1 + Version: 5.10.0 { Anchor(base, .leading) } var trailing: Anchor { Anchor(base, .trailing) } + var topMargin: Anchor { Anchor(base, .topMargin) } + var bottomMargin: Anchor { Anchor(base, .bottomMargin) } + var leftMargin: Anchor { Anchor(base, .leftMargin) } + var rightMargin: Anchor { Anchor(base, .rightMargin) } + var leadingMargin: Anchor { Anchor(base, .leadingMargin) } + var trailingMargin: Anchor { Anchor(base, .trailingMargin) } + var centerX: Anchor { Anchor(base, .centerX) } var centerY: Anchor { Anchor(base, .centerY) } diff --git a/Sources/BrazeUI/Dependencies/UIKitExt.swift b/Sources/BrazeUI/Dependencies/UIKitExt.swift index e8a6256cbc..7cc246bc74 100644 --- a/Sources/BrazeUI/Dependencies/UIKitExt.swift +++ b/Sources/BrazeUI/Dependencies/UIKitExt.swift @@ -87,8 +87,11 @@ extension UIResponder { extension String { - func attributed(_ setup: (NSMutableParagraphStyle) -> Void) -> NSAttributedString { - let attributedText = NSMutableAttributedString(string: self) + func attributed( + with attributes: [NSAttributedString.Key: Any]? = nil, + _ setup: (NSMutableParagraphStyle) -> Void + ) -> NSAttributedString { + let attributedText = NSMutableAttributedString(string: self, attributes: attributes) let style = NSMutableParagraphStyle() setup(style) attributedText.addAttribute( diff --git a/Sources/BrazeUI/InAppMessageUI/Views/InAppMessageUIModalView.swift b/Sources/BrazeUI/InAppMessageUI/Views/InAppMessageUIModalView.swift index c1b2bd92eb..8cfad91d4f 100644 --- a/Sources/BrazeUI/InAppMessageUI/Views/InAppMessageUIModalView.swift +++ b/Sources/BrazeUI/InAppMessageUI/Views/InAppMessageUIModalView.swift @@ -1,6 +1,17 @@ import BrazeKit import UIKit +struct TextStyle: Equatable { + var color: UIColor + var font: UIFont +} + +struct TextViewStyle: Equatable { + var header: TextStyle + var message: TextStyle + var headerMessageSpacing: Double +} + extension BrazeInAppMessageUI { /// The view for modal in-app messages. @@ -114,10 +125,10 @@ extension BrazeInAppMessageUI { bottom: padding.bottom, right: 0 ) - textStack.layoutMargins = .init( - top: 0, + textContainer.layoutMargins = .init( + top: TextViewLayoutConstants.textContainerLayoutMargins.top, left: padding.left, - bottom: 0, + bottom: TextViewLayoutConstants.textContainerLayoutMargins.bottom, right: padding.right ) buttonsContainer?.stack.layoutMargins = .init( @@ -128,12 +139,12 @@ extension BrazeInAppMessageUI { ) // Spacings - textStack.spacing = attributes.labelsSpacing + textViewStyle.headerMessageSpacing = attributes.labelsSpacing contentView.stack.spacing = attributes.spacing // Fonts - headerLabel.font = attributes.headerFont - messageLabel.font = attributes.messageFont + textViewStyle.header.font = attributes.headerFont + textViewStyle.message.font = attributes.messageFont // Corner radius shadowView.layer.cornerRadius = attributes.cornerRadius @@ -187,42 +198,30 @@ extension BrazeInAppMessageUI { } }() - public lazy var headerLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 0 - label.adjustsFontForContentSizeCategory = true - label.attributedText = message.header.attributed { - $0.lineSpacing = 2 - $0.alignment = message.headerTextAlignment.nsTextAlignment(forTraits: traitCollection) - } - label.setContentCompressionResistancePriority(.required, for: .vertical) - label.setContentHuggingPriority(.required, for: .vertical) - return label - }() - - public lazy var messageLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 0 - label.adjustsFontForContentSizeCategory = true - label.attributedText = message.message.attributed { - $0.lineSpacing = 4 - $0.alignment = message.messageTextAlignment.nsTextAlignment(forTraits: traitCollection) - } - label.setContentCompressionResistancePriority(.required, for: .vertical) - label.setContentHuggingPriority(.required, for: .vertical) - return label - }() - - public lazy var textStack: UIStackView = { - let stack = UIStackView(arrangedSubviews: [headerLabel, messageLabel]) - stack.axis = .vertical - stack.isLayoutMarginsRelativeArrangement = true - return stack + public lazy var textView: UITextView = { + let textView = UITextView() + // Config defaults: + textView.backgroundColor = .clear + textView.isEditable = false + textView.isSelectable = false + textView.adjustsFontForContentSizeCategory = true + + // Don't allow scrolling; the textview's parent (a scrollview) will control that. + // This will force the textView to render its full content height, + // and the textViewContainer can then take that as its content and scroll it for us. + // (This hot tip brought to you by https://archive.is/fMUR7 and many other results.) + textView.isScrollEnabled = false + + // Layout defaults: + textView.setContentCompressionResistancePriority(.required, for: .vertical) + textView.setContentHuggingPriority(.required, for: .vertical) + + return textView }() public lazy var textContainer: UIScrollView = { let container = UIScrollView() - container.addSubview(textStack) + container.addSubview(textView) return container }() @@ -295,6 +294,18 @@ extension BrazeInAppMessageUI { self.gifViewProvider = gifViewProvider self.presented = presented + self.textViewStyle = TextViewStyle( + header: .init( + color: .clear, + font: attributes.headerFont + ), + message: .init( + color: .clear, + font: attributes.messageFont + ), + headerMessageSpacing: attributes.labelsSpacing + ) + super.init(frame: .zero) addSubview(shadowView) @@ -320,8 +331,8 @@ extension BrazeInAppMessageUI { public var theme: Braze.InAppMessage.Theme { message.theme(for: traitCollection) } open func applyTheme() { - headerLabel.textColor = theme.headerTextColor.uiColor - messageLabel.textColor = theme.textColor.uiColor + textViewStyle.header.color = theme.headerTextColor.uiColor + textViewStyle.message.color = theme.textColor.uiColor closeButton.setTitleColor(theme.closeButtonColor.uiColor, for: .normal) contentView.backgroundColor = theme.backgroundColor.uiColor backgroundColor = theme.frameColor.uiColor @@ -372,14 +383,18 @@ extension BrazeInAppMessageUI { break } - // Text - textStack.anchors.edges.pin() - textStack.anchors.width.equal(textContainer.anchors.width) - // - not required priority to allow the text container scrollview to shrink - textStack.anchors.height.lessThanOrEqual(textContainer.anchors.height).priority = + // Anchor the text view's layout using margins from its parent container: + textView.anchors.leading.equal(textContainer.anchors.leadingMargin) + textView.anchors.trailing.equal(textContainer.anchors.trailingMargin) + textView.anchors.top.equal(textContainer.anchors.topMargin) + textView.anchors.bottom.equal(textContainer.anchors.bottomMargin) + textView.anchors.width.equal(textContainer.layoutMarginsGuide.anchors.width) + // Add a not-required priority to allow the text container scrollview to shrink + textView.anchors.height.lessThanOrEqual(textContainer.layoutMarginsGuide.anchors.height) + .priority = .defaultHigh - let textStackHeightConstraint = textStack.anchors.height.equal(textContainer.anchors.height) - textStackHeightConstraint.priority = .defaultHigh - 1 + let textViewHeightConstraint = textView.anchors.height.equal(textContainer.anchors.height) + textViewHeightConstraint.priority = .defaultHigh - 1 // Close button closeButton.anchors.height.equal(closeButton.anchors.width) @@ -492,8 +507,68 @@ extension BrazeInAppMessageUI { } dismiss() } + + // MARK: - Text view style + + private var textViewStyle: TextViewStyle { + didSet { + if oldValue != textViewStyle { + self.updateTextViewContent() + } + } + } + + private func updateTextViewContent() { + let textViewText = NSMutableAttributedString() + + textViewText.append( + message.header.attributed( + with: [ + NSAttributedString.Key.font: textViewStyle.header.font, + NSAttributedString.Key.foregroundColor: textViewStyle.header.color, + ], + { + $0.lineSpacing = 2 * TextViewLayoutConstants.headerLineSpacingScaleFactor + $0.alignment = message.headerTextAlignment.nsTextAlignment(forTraits: traitCollection) + $0.paragraphSpacing = max( + 0.0, + textViewStyle.headerMessageSpacing + - TextViewLayoutConstants.headerMessageSpacingOffset) + }) + ) + // Users don't add newlines to the end of their header text + // Insert a newline between header and message, but not with the possible extra styling of the header (e.g. font size, etc.) + // The paragraph spacing set above will occur between the header text and this linebreak. + textViewText.append(NSAttributedString(string: "\n")) + + textViewText.append( + message.message.attributed( + with: [ + NSAttributedString.Key.font: textViewStyle.message.font, + NSAttributedString.Key.foregroundColor: textViewStyle.message.color, + ], + { + $0.lineSpacing = 4 * TextViewLayoutConstants.messageLineSpacingScaleFactor + $0.alignment = message.messageTextAlignment.nsTextAlignment(forTraits: traitCollection) + }) + ) + + textView.attributedText = textViewText + } } + private enum TextViewLayoutConstants { + // Manually-tuned values to get us close to our previous StackView+Label appearance. + + // Soak up some vertical space that UITextView leaves above and below its text: + static let textContainerLayoutMargins = UIEdgeInsets(top: -8, left: 0, bottom: -8, right: 0) + // Scale factors for label → textview line spacing: + static let headerLineSpacingScaleFactor = 0.78 + static let messageLineSpacingScaleFactor = 0.47 + // Subtraction offset between header and message: + // (textview/TextKit renders a tiny bit of extra ascender+descender space that we want to eat up) + static let headerMessageSpacingOffset: Double = 1.0 + } } // MARK: - Previews diff --git a/Sources/BrazeUICompat/ABKInAppMessage/ViewControllers/ABKInAppMessageViewController.m b/Sources/BrazeUICompat/ABKInAppMessage/ViewControllers/ABKInAppMessageViewController.m index 85b014d790..eb4991c055 100644 --- a/Sources/BrazeUICompat/ABKInAppMessage/ViewControllers/ABKInAppMessageViewController.m +++ b/Sources/BrazeUICompat/ABKInAppMessage/ViewControllers/ABKInAppMessageViewController.m @@ -56,6 +56,7 @@ - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil); + [[ABKUIUtils activeApplicationViewController] setNeedsStatusBarAppearanceUpdate]; } - (BOOL)prefersStatusBarHidden {