Skip to content

Instantly share code, notes, and snippets.

@frosty
Created August 20, 2018 13:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frosty/854b0bd95ede6d362edb3f9cddf7f360 to your computer and use it in GitHub Desktop.
Save frosty/854b0bd95ede6d362edb3f9cddf7f360 to your computer and use it in GitHub Desktop.
diff --git a/WordPress/Classes/Stores/NoticeStore.swift b/WordPress/Classes/Stores/NoticeStore.swift
index 42dcb72422..58ec7a000b 100644
--- a/WordPress/Classes/Stores/NoticeStore.swift
+++ b/WordPress/Classes/Stores/NoticeStore.swift
@@ -22,7 +22,7 @@ struct Notice {
/// Style used to configure visual style when displayed
///
- let style: Style
+ let style: NoticeStyle
/// A title for an optional action button that can be displayed as part of
/// a notice
@@ -32,7 +32,7 @@ struct Notice {
/// is tapped, if you've provided an action title
let actionHandler: (() -> Void)?
- init(title: String, message: String? = nil, feedbackType: UINotificationFeedbackType? = nil, notificationInfo: NoticeNotificationInfo? = nil, style: Style = .normal) {
+ init(title: String, message: String? = nil, feedbackType: UINotificationFeedbackType? = nil, notificationInfo: NoticeNotificationInfo? = nil, style: NoticeStyle = NormalNoticeStyle()) {
self.title = title
self.message = message
self.feedbackType = feedbackType
@@ -42,7 +42,7 @@ struct Notice {
self.style = style
}
- init(title: String, message: String? = nil, feedbackType: UINotificationFeedbackType? = nil, notificationInfo: NoticeNotificationInfo? = nil, style: Style = .normal, actionTitle: String, actionHandler: @escaping (() -> Void)) {
+ init(title: String, message: String? = nil, feedbackType: UINotificationFeedbackType? = nil, notificationInfo: NoticeNotificationInfo? = nil, style: NoticeStyle = NormalNoticeStyle(), actionTitle: String, actionHandler: @escaping (() -> Void)) {
self.title = title
self.message = message
self.feedbackType = feedbackType
@@ -51,11 +51,6 @@ struct Notice {
self.actionHandler = actionHandler
self.style = style
}
-
- public enum Style {
- case normal
- case quickStart(NSAttributedString?)
- }
}
struct NoticeNotificationInfo {
@@ -87,6 +82,53 @@ struct NoticeNotificationInfo {
}
}
+public protocol NoticeStyle {
+ // Text
+ var attributedMessage: NSAttributedString? { get }
+
+ // Fonts
+ var titleLabelFont: UIFont { get }
+ var messageLabelFont: UIFont { get }
+ var actionButtonFont: UIFont? { get }
+
+ // Colors
+ var titleColor: UIColor { get }
+ var messageColor: UIColor { get }
+ var backgroundColor: UIColor { get }
+
+ // Misc
+ var isDismissable: Bool { get }
+}
+
+
+public struct NormalNoticeStyle: NoticeStyle {
+ public let attributedMessage: NSAttributedString? = nil
+
+ public let titleLabelFont = UIFont.boldSystemFont(ofSize: 14.0)
+ public let messageLabelFont = UIFont.systemFont(ofSize: 14.0)
+ public let actionButtonFont: UIFont? = UIFont.systemFont(ofSize: 14.0)
+
+ public let titleColor: UIColor = WPStyleGuide.darkGrey()
+ public let messageColor: UIColor = WPStyleGuide.darkGrey()
+ public let backgroundColor: UIColor = .clear
+
+ public let isDismissable = true
+}
+
+public struct QuickStartNoticeStyle: NoticeStyle {
+ public let attributedMessage: NSAttributedString?
+
+ public let titleLabelFont = WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold)
+ public let messageLabelFont = WPStyleGuide.fontForTextStyle(.subheadline)
+ public let actionButtonFont: UIFont? = nil
+
+ public let titleColor: UIColor = .white
+ public let messageColor: UIColor = WPStyleGuide.greyLighten20()
+ public let backgroundColor: UIColor = WPStyleGuide.darkGrey().withAlphaComponent(0.88)
+
+ public let isDismissable = false
+}
+
/// NoticeActions can be posted to control or report the display of notices.
///
enum NoticeAction: Action {
diff --git a/WordPress/Classes/ViewRelated/Blog/QuickStartTourGuide.swift b/WordPress/Classes/ViewRelated/Blog/QuickStartTourGuide.swift
index 9c40af1468..41d79c9564 100644
--- a/WordPress/Classes/ViewRelated/Blog/QuickStartTourGuide.swift
+++ b/WordPress/Classes/ViewRelated/Blog/QuickStartTourGuide.swift
@@ -16,8 +16,9 @@ open class QuickStartTourGuide: NSObject, UINavigationControllerDelegate {
// MARK: Quick Start methods
@objc
func showTestQuickStartNotice() {
- let exampleLabelStr = QuickStartNoticeView.makeHighlightMessage(base: "Tap %@ to see your checklist", highlight: "Quick Start", icon: Gridicon.iconOfType(.listCheckmark))
- let notice = Notice(title: "Test Quick Start Notice", style: .quickStart(exampleLabelStr))
+ let exampleMessage = "Tap %@ to see your checklist".highlightingPhrase("Quick Start",
+ withIcon: Gridicon.iconOfType(.listCheckmark))
+ let notice = Notice(title: "Test Quick Start Notice", style: QuickStartNoticeStyle(attributedMessage: exampleMessage))
ActionDispatcher.dispatch(NoticeAction.post(notice))
}
@@ -43,3 +44,51 @@ open class QuickStartTourGuide: NSObject, UINavigationControllerDelegate {
QuickStartFollowTour()
]
}
+
+private extension String {
+ func highlightingPhrase(_ phrase: String, withIcon icon: UIImage) -> NSAttributedString {
+ let normalParts = components(separatedBy: "%@")
+ guard normalParts.count > 0 else {
+ // if the provided base doesn't contain %@ then we don't know where to place the highlight
+ return NSAttributedString(string: self)
+ }
+ let resultString = NSMutableAttributedString(string: normalParts[0])
+
+ let font = WPStyleGuide.mediumWeightFont(forStyle: .subheadline)
+
+ let iconAttachment = NSTextAttachment()
+ iconAttachment.image = icon.imageWithTintColor(HighlightConstants.highlightColor)
+ iconAttachment.bounds = CGRect(x: 0.0, y: font.descender + HighlightConstants.iconOffset, width: HighlightConstants.iconSize, height: HighlightConstants.iconSize)
+ let iconStr = NSAttributedString(attachment: iconAttachment)
+
+ let highlightStr = NSAttributedString(string: phrase, attributes: [.foregroundColor: HighlightConstants.highlightColor, .font: HighlightConstants.highlightFont])
+
+ switch UIView.userInterfaceLayoutDirection(for: .unspecified) {
+ case .rightToLeft:
+ resultString.append(highlightStr)
+ resultString.append(NSAttributedString(string: " "))
+ resultString.append(iconStr)
+ default:
+ resultString.append(iconStr)
+ resultString.append(NSAttributedString(string: " "))
+ resultString.append(highlightStr)
+ }
+
+ if normalParts.count > 1 {
+ resultString.append(NSAttributedString(string: normalParts[1]))
+ }
+
+ return resultString
+ }
+
+ private enum HighlightConstants {
+ static let iconOffset: CGFloat = 1.0
+ static let iconSize: CGFloat = 16.0
+ static let highlightColor = WPStyleGuide.lightBlue()
+ static var highlightFont: UIFont {
+ get {
+ return WPStyleGuide.fontForTextStyle(.subheadline, fontWeight: .semibold)
+ }
+ }
+ }
+}
diff --git a/WordPress/Classes/ViewRelated/System/Notices/NoticePresenter.swift b/WordPress/Classes/ViewRelated/System/Notices/NoticePresenter.swift
index 5346361330..927064bcdc 100644
--- a/WordPress/Classes/ViewRelated/System/Notices/NoticePresenter.swift
+++ b/WordPress/Classes/ViewRelated/System/Notices/NoticePresenter.swift
@@ -65,7 +65,7 @@ class NoticePresenter: NSObject {
generator.prepare()
- let noticeView = makeNoticeView(for: notice)
+ let noticeView = NoticeView(notice: notice)
noticeView.translatesAutoresizingMaskIntoConstraints = false
let noticeContainerView = NoticeContainerView(noticeView: noticeView)
@@ -95,7 +95,7 @@ class NoticePresenter: NSObject {
animatePresentation(fromState: fromState, toState: toState, completion: {
// Quick Start notices don't get automatically dismissed
- if case .quickStart(_) = notice.style {
+ guard notice.style.isDismissable else {
return
}
@@ -103,15 +103,6 @@ class NoticePresenter: NSObject {
})
}
- private func makeNoticeView(for notice: Notice) -> NoticeView {
- switch notice.style {
- case .quickStart(let formattedMessage):
- return QuickStartNoticeView(notice: notice)
- default:
- return NoticeView(notice: notice)
- }
- }
-
private func offscreenState(for noticeContainer: NoticeContainerView) -> (() -> ()) {
return { [weak self] in
guard let strongSelf = self,
diff --git a/WordPress/Classes/ViewRelated/System/Notices/NoticeView.swift b/WordPress/Classes/ViewRelated/System/Notices/NoticeView.swift
index d7210b2f24..aecdeef85c 100644
--- a/WordPress/Classes/ViewRelated/System/Notices/NoticeView.swift
+++ b/WordPress/Classes/ViewRelated/System/Notices/NoticeView.swift
@@ -1,22 +1,22 @@
import UIKit
class NoticeView: UIView {
- internal let contentStackView = UIStackView()
- internal let backgroundContainerView = UIView()
- internal let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight))
- internal let actionBackgroundView = UIView()
+ private let contentStackView = UIStackView()
+ private let backgroundContainerView = UIView()
+ private let backgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .extraLight))
+ private let actionBackgroundView = UIView()
private let shadowLayer = CAShapeLayer()
private let shadowMaskLayer = CAShapeLayer()
- internal let titleLabel = UILabel()
- internal let messageLabel = UILabel()
+ private let titleLabel = UILabel()
+ private let messageLabel = UILabel()
private let actionButton = UIButton(type: .system)
- internal let notice: Notice
+ private let notice: Notice
var dismissHandler: (() -> Void)?
- required init(notice: Notice) {
+ init(notice: Notice) {
self.notice = notice
super.init(frame: .zero)
@@ -29,17 +29,33 @@ class NoticeView: UIView {
}
/// configure the NoticeView for display
- internal func configure() {
+ private func configure() {
configureBackgroundViews()
configureShadow()
configureContentStackView()
configureLabels()
- configureActionButton()
- configureDismissRecognizer()
+
+ if notice.actionTitle != nil {
+ configureActionButton()
+ }
+
+ if notice.style.isDismissable {
+ configureDismissRecognizer()
+ }
+
configureForNotice()
}
private func configureBackgroundViews() {
+ if notice.style.backgroundColor != .clear {
+ let backgroundColorView = UIView()
+ backgroundColorView.backgroundColor = notice.style.backgroundColor
+ backgroundView.contentView.addSubview(backgroundColorView)
+ backgroundColorView.layer.cornerRadius = Metrics.cornerRadius
+ backgroundColorView.translatesAutoresizingMaskIntoConstraints = false
+ backgroundView.contentView.pinSubviewToAllEdges(backgroundColorView)
+ }
+
addSubview(backgroundContainerView)
backgroundContainerView.translatesAutoresizingMaskIntoConstraints = false
pinSubviewToAllEdges(backgroundContainerView)
@@ -52,7 +68,7 @@ class NoticeView: UIView {
backgroundContainerView.layer.masksToBounds = true
}
- internal func configureShadow() {
+ private func configureShadow() {
shadowLayer.shadowPath = UIBezierPath(roundedRect: layer.bounds, cornerRadius: Metrics.cornerRadius).cgPath
shadowLayer.shadowColor = Appearance.shadowColor.cgColor
shadowLayer.shadowOpacity = Appearance.shadowOpacity
@@ -82,14 +98,14 @@ class NoticeView: UIView {
shadowMaskLayer.path = maskPath
}
- internal func configureContentStackView() {
+ private func configureContentStackView() {
contentStackView.axis = .horizontal
contentStackView.translatesAutoresizingMaskIntoConstraints = false
backgroundView.contentView.addSubview(contentStackView)
backgroundView.contentView.pinSubviewToAllEdges(contentStackView)
}
- private func configureLabels() { //**
+ private func configureLabels() {
let labelStackView = UIStackView()
labelStackView.translatesAutoresizingMaskIntoConstraints = false
labelStackView.alignment = .leading
@@ -109,11 +125,11 @@ class NoticeView: UIView {
labelStackView.bottomAnchor.constraint(equalTo: backgroundView.contentView.bottomAnchor)
])
- titleLabel.font = Fonts.titleLabelFont
- messageLabel.font = Fonts.messageLabelFont
+ titleLabel.font = notice.style.titleLabelFont
+ messageLabel.font = notice.style.messageLabelFont
- titleLabel.textColor = WPStyleGuide.darkGrey()
- messageLabel.textColor = WPStyleGuide.darkGrey()
+ titleLabel.textColor = notice.style.titleColor
+ messageLabel.textColor = notice.style.messageColor
}
private func configureActionButton() {
@@ -133,7 +149,7 @@ class NoticeView: UIView {
actionBackgroundView.pinSubviewToAllEdgeMargins(actionButton)
- actionButton.titleLabel?.font = Fonts.actionButtonFont
+ actionButton.titleLabel?.font = notice.style.actionButtonFont
actionButton.setTitleColor(WPStyleGuide.mediumBlue(), for: .normal)
actionButton.addTarget(self, action: #selector(actionButtonTapped), for: .touchUpInside)
actionButton.setContentCompressionResistancePriority(.required, for: .horizontal)
@@ -147,7 +163,10 @@ class NoticeView: UIView {
private func configureForNotice() {
titleLabel.text = notice.title
- if let message = notice.message {
+ if let attributedMessage = notice.style.attributedMessage {
+ messageLabel.attributedText = attributedMessage
+ titleLabel.isHidden = true
+ } else if let message = notice.message {
messageLabel.text = message
} else {
titleLabel.numberOfLines = 2
@@ -183,12 +202,6 @@ class NoticeView: UIView {
static let labelLineSpacing: CGFloat = 18.0
}
- private enum Fonts {
- static let actionButtonFont = UIFont.systemFont(ofSize: 14.0)
- static let titleLabelFont = UIFont.boldSystemFont(ofSize: 14.0)
- static let messageLabelFont = UIFont.systemFont(ofSize: 14.0)
- }
-
private enum Appearance {
static let actionBackgroundColor = UIColor.white.withAlphaComponent(0.5)
static let shadowColor: UIColor = .black
diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj
index 1403caef22..7878c7fa40 100644
--- a/WordPress/WordPress.xcodeproj/project.pbxproj
+++ b/WordPress/WordPress.xcodeproj/project.pbxproj
@@ -237,7 +237,6 @@
433432521E9ED18900915988 /* LoginEpilogueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433432511E9ED18900915988 /* LoginEpilogueViewController.swift */; };
433432541E9EE0B100915988 /* EpilogueSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433432531E9EE0B100915988 /* EpilogueSegue.swift */; };
433A550220F693A100757928 /* ManyDelegateNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433A550120F693A100757928 /* ManyDelegateNavigationController.swift */; };
- 433A550420F6AC5600757928 /* QuickStartNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 433A550320F6AC5600757928 /* QuickStartNoticeView.swift */; };
4348C88321002FBD00735DC0 /* QuickStartTourGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4348C88221002FBD00735DC0 /* QuickStartTourGuide.swift */; };
435035971EDCAB99004ECF47 /* jetpack.json in Resources */ = {isa = PBXBuildFile; fileRef = 435035921EDCAB99004ECF47 /* jetpack.json */; };
435035981EDCAB99004ECF47 /* notifications.json in Resources */ = {isa = PBXBuildFile; fileRef = 435035931EDCAB99004ECF47 /* notifications.json */; };
@@ -1735,7 +1734,6 @@
433432511E9ED18900915988 /* LoginEpilogueViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginEpilogueViewController.swift; sourceTree = "<group>"; };
433432531E9EE0B100915988 /* EpilogueSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EpilogueSegue.swift; sourceTree = "<group>"; };
433A550120F693A100757928 /* ManyDelegateNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManyDelegateNavigationController.swift; sourceTree = "<group>"; };
- 433A550320F6AC5600757928 /* QuickStartNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickStartNoticeView.swift; sourceTree = "<group>"; };
4348C88221002FBD00735DC0 /* QuickStartTourGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickStartTourGuide.swift; sourceTree = "<group>"; };
435035921EDCAB99004ECF47 /* jetpack.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = jetpack.json; sourceTree = "<group>"; };
435035931EDCAB99004ECF47 /* notifications.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = notifications.json; sourceTree = "<group>"; };
@@ -3915,7 +3913,6 @@
isa = PBXGroup;
children = (
4348C88221002FBD00735DC0 /* QuickStartTourGuide.swift */,
- 433A550320F6AC5600757928 /* QuickStartNoticeView.swift */,
4395A1582106389800844E8E /* QuickStartTours.swift */,
4395A15A21066DCD00844E8E /* QuickStartChecklistCell.xib */,
4395A15C2106718900844E8E /* QuickStartChecklistCell.swift */,
@@ -8079,7 +8076,6 @@
17BEE0041FC867C20074C124 /* WordPressAppDelegate+Swift.swift in Sources */,
08472A201C727E020040769D /* PostServiceOptions.m in Sources */,
7E4123BA20F4097B00DF8486 /* FormattableContentGroup.swift in Sources */,
- 433A550420F6AC5600757928 /* QuickStartNoticeView.swift in Sources */,
08216FD21CDBF96000304BA7 /* MenuItemSourceViewController.m in Sources */,
74558369201A1FD3007809BB /* WPReusableTableViewCells.swift in Sources */,
E6DAABDD1CF632EC0069D933 /* ReaderSearchSuggestionsViewController.swift in Sources */,
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment