Merged PR 203007: MSBadgeView with MSBadgeBaseView
This PR introduces MSBadgeView with MSBadgeBaseView to represent a "badge". This badge will be used in an upcoming field to represent selected personas from the MSPersonaListView. The badge can be selected with a tap and then tapped again once in a selected state to reveal more details about the selected person. It includes 3 styles: default, error, and warning with a disabled state as well. Also included in this PR: - A small update to the accessibility for MSAvatarView to become an accessibility element so details about the avatar can be used in VoiceOver when the avatar view is used outside of a MSPersonaCell. - An update to Physical colors in MSColors to include dark yellow colors for 'warning' and 'light warning'. The previous red colors used for 'warning' and 'light warning' are now named 'error' and 'lightError' respectively. ![Screen Shot 2018-11-14 at 3.56.30 PM.png](https://onedrive.visualstudio.com/4dcbf0bc-c3cd-49c8-a7c3-ec1924691d9b/_apis/git/repositories/93ac71ee-b53a-4fc6-a8c4-d46a80d4ca39/pullRequests/203007/attachments/Screen%20Shot%202018-11-14%20at%203.56.30%20PM.png) Related work items: #627343
This commit is contained in:
Родитель
a7c3bdb2db
Коммит
515a798eb5
|
@ -7,6 +7,7 @@ import UIKit
|
||||||
// Register your control demos here
|
// Register your control demos here
|
||||||
let demos: [(title: String, controllerClass: UIViewController.Type)] = [
|
let demos: [(title: String, controllerClass: UIViewController.Type)] = [
|
||||||
("MSAvatarView", MSAvatarViewDemoController.self),
|
("MSAvatarView", MSAvatarViewDemoController.self),
|
||||||
|
("MSBadgeView", MSBadgeViewDemoController.self),
|
||||||
("MSDatePicker", MSDatePickerDemoController.self),
|
("MSDatePicker", MSDatePickerDemoController.self),
|
||||||
("MSDrawerController", MSDrawerDemoController.self),
|
("MSDrawerController", MSDrawerDemoController.self),
|
||||||
("MSLabel", MSLabelDemoController.self),
|
("MSLabel", MSLabelDemoController.self),
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import OfficeUIFabric
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class MSBadgeViewDemoController: DemoController {
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
container.alignment = .leading
|
||||||
|
|
||||||
|
addTitle(text: "Default badge")
|
||||||
|
addBadge(text: "Kat Larrson", style: .default)
|
||||||
|
container.addArrangedSubview(UIView())
|
||||||
|
|
||||||
|
addTitle(text: "Error badge")
|
||||||
|
addBadge(text: "Allan Munger", style: .error)
|
||||||
|
container.addArrangedSubview(UIView())
|
||||||
|
|
||||||
|
addTitle(text: "Warning badge")
|
||||||
|
addBadge(text: "Mona Kane", style: .warning)
|
||||||
|
container.addArrangedSubview(UIView())
|
||||||
|
|
||||||
|
addTitle(text: "Disabled badge")
|
||||||
|
addBadge(text: "Mauricio August", style: .default, isEnabled: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addTitle(text: String) {
|
||||||
|
let titleLabel = MSLabel(style: .subhead, colorStyle: .regular)
|
||||||
|
titleLabel.text = text
|
||||||
|
container.addArrangedSubview(titleLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addBadge(text: String, style: MSBadgeViewStyle, isEnabled: Bool = true) {
|
||||||
|
let data = MSBadgeViewDataSource(text: text, style: style)
|
||||||
|
let badge = MSBadgeView()
|
||||||
|
badge.dataSource = data
|
||||||
|
badge.delegate = self
|
||||||
|
badge.isEnabled = isEnabled
|
||||||
|
container.addArrangedSubview(badge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MSBadgeViewDemoController: MSBadgeBaseViewDelegate {
|
||||||
|
func didSelectBadge(_ badge: MSBadgeBaseView) { }
|
||||||
|
|
||||||
|
func didTapSelectedBadge(_ badge: MSBadgeBaseView) {
|
||||||
|
badge.isSelected = false
|
||||||
|
let alert = UIAlertController(title: "A selected badge was tapped", message: nil, preferredStyle: .alert)
|
||||||
|
let action = UIAlertAction(title: "OK", style: .default)
|
||||||
|
alert.addAction(action)
|
||||||
|
present(alert, animated: true)
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,6 +47,8 @@ extension MSTextColorStyle {
|
||||||
return "White"
|
return "White"
|
||||||
case .primary:
|
case .primary:
|
||||||
return "Primary"
|
return "Primary"
|
||||||
|
case .error:
|
||||||
|
return "Error"
|
||||||
case .warning:
|
case .warning:
|
||||||
return "Warning"
|
return "Warning"
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,11 @@
|
||||||
B42661422148568800E25423 /* MSAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B42661412148568800E25423 /* MSAvatarView.swift */; };
|
B42661422148568800E25423 /* MSAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B42661412148568800E25423 /* MSAvatarView.swift */; };
|
||||||
B444D6AD218126C90002B4D4 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B444D6AC218126C90002B4D4 /* Localizable.stringsdict */; };
|
B444D6AD218126C90002B4D4 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B444D6AC218126C90002B4D4 /* Localizable.stringsdict */; };
|
||||||
B444D6B12181403C0002B4D4 /* UITableViewCell+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */; };
|
B444D6B12181403C0002B4D4 /* UITableViewCell+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */; };
|
||||||
|
B444D6B62183A9740002B4D4 /* MSBadgeBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B444D6B52183A9740002B4D4 /* MSBadgeBaseView.swift */; };
|
||||||
B46D3F91215056940029772C /* CharacterSet+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F90215056940029772C /* CharacterSet+Extension.swift */; };
|
B46D3F91215056940029772C /* CharacterSet+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F90215056940029772C /* CharacterSet+Extension.swift */; };
|
||||||
B46D3F932151D95F0029772C /* MSPersonaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F922151D95F0029772C /* MSPersonaCell.swift */; };
|
B46D3F932151D95F0029772C /* MSPersonaCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F922151D95F0029772C /* MSPersonaCell.swift */; };
|
||||||
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F9C215985AC0029772C /* MSPersonaListView.swift */; };
|
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B46D3F9C215985AC0029772C /* MSPersonaListView.swift */; };
|
||||||
|
B487EA7321911FD4005E90B3 /* MSBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B487EA7221911FD4005E90B3 /* MSBadgeView.swift */; };
|
||||||
B4E782C12176AD5E00A7DFCE /* MSActionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C02176AD5E00A7DFCE /* MSActionsCell.swift */; };
|
B4E782C12176AD5E00A7DFCE /* MSActionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C02176AD5E00A7DFCE /* MSActionsCell.swift */; };
|
||||||
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C221793AB200A7DFCE /* MSActivityIndicatorCell.swift */; };
|
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C221793AB200A7DFCE /* MSActivityIndicatorCell.swift */; };
|
||||||
B4E782C521793BB900A7DFCE /* MSActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */; };
|
B4E782C521793BB900A7DFCE /* MSActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */; };
|
||||||
|
@ -127,9 +129,11 @@
|
||||||
B42661412148568800E25423 /* MSAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAvatarView.swift; sourceTree = "<group>"; };
|
B42661412148568800E25423 /* MSAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSAvatarView.swift; sourceTree = "<group>"; };
|
||||||
B444D6AC218126C90002B4D4 /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = "<group>"; };
|
B444D6AC218126C90002B4D4 /* Localizable.stringsdict */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extension.swift"; sourceTree = "<group>"; };
|
B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extension.swift"; sourceTree = "<group>"; };
|
||||||
|
B444D6B52183A9740002B4D4 /* MSBadgeBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBadgeBaseView.swift; sourceTree = "<group>"; };
|
||||||
B46D3F90215056940029772C /* CharacterSet+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Extension.swift"; sourceTree = "<group>"; };
|
B46D3F90215056940029772C /* CharacterSet+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Extension.swift"; sourceTree = "<group>"; };
|
||||||
B46D3F922151D95F0029772C /* MSPersonaCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPersonaCell.swift; sourceTree = "<group>"; };
|
B46D3F922151D95F0029772C /* MSPersonaCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPersonaCell.swift; sourceTree = "<group>"; };
|
||||||
B46D3F9C215985AC0029772C /* MSPersonaListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPersonaListView.swift; sourceTree = "<group>"; };
|
B46D3F9C215985AC0029772C /* MSPersonaListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPersonaListView.swift; sourceTree = "<group>"; };
|
||||||
|
B487EA7221911FD4005E90B3 /* MSBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSBadgeView.swift; sourceTree = "<group>"; };
|
||||||
B4E782C02176AD5E00A7DFCE /* MSActionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActionsCell.swift; sourceTree = "<group>"; };
|
B4E782C02176AD5E00A7DFCE /* MSActionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActionsCell.swift; sourceTree = "<group>"; };
|
||||||
B4E782C221793AB200A7DFCE /* MSActivityIndicatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActivityIndicatorCell.swift; sourceTree = "<group>"; };
|
B4E782C221793AB200A7DFCE /* MSActivityIndicatorCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActivityIndicatorCell.swift; sourceTree = "<group>"; };
|
||||||
B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActivityIndicatorView.swift; sourceTree = "<group>"; };
|
B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSActivityIndicatorView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -310,6 +314,8 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */,
|
B4E782C421793BB900A7DFCE /* MSActivityIndicatorView.swift */,
|
||||||
|
B444D6B52183A9740002B4D4 /* MSBadgeBaseView.swift */,
|
||||||
|
B487EA7221911FD4005E90B3 /* MSBadgeView.swift */,
|
||||||
FDA1AF8B21484625001AE720 /* MSBlurringView.swift */,
|
FDA1AF8B21484625001AE720 /* MSBlurringView.swift */,
|
||||||
A5CEC23020E451D00016922A /* MSButton.swift */,
|
A5CEC23020E451D00016922A /* MSButton.swift */,
|
||||||
A5B87B03211E22B70038C37C /* MSDimmingView.swift */,
|
A5B87B03211E22B70038C37C /* MSDimmingView.swift */,
|
||||||
|
@ -563,6 +569,7 @@
|
||||||
A5B87B06211E23650038C37C /* UIView+Extensions.swift in Sources */,
|
A5B87B06211E23650038C37C /* UIView+Extensions.swift in Sources */,
|
||||||
A5961FA7218A2E4500E2A506 /* UIImage+Extensions.swift in Sources */,
|
A5961FA7218A2E4500E2A506 /* UIImage+Extensions.swift in Sources */,
|
||||||
B42661422148568800E25423 /* MSAvatarView.swift in Sources */,
|
B42661422148568800E25423 /* MSAvatarView.swift in Sources */,
|
||||||
|
B444D6B62183A9740002B4D4 /* MSBadgeBaseView.swift in Sources */,
|
||||||
FD7254E72146E946002F4069 /* MSCalendarViewDayCell.swift in Sources */,
|
FD7254E72146E946002F4069 /* MSCalendarViewDayCell.swift in Sources */,
|
||||||
A589F854211BA03200471C23 /* MSLabel.swift in Sources */,
|
A589F854211BA03200471C23 /* MSLabel.swift in Sources */,
|
||||||
B4266140214852B400E25423 /* NSString+Extension.swift in Sources */,
|
B4266140214852B400E25423 /* NSString+Extension.swift in Sources */,
|
||||||
|
@ -575,6 +582,7 @@
|
||||||
FDA1AF8F21484A26001AE720 /* Obscurable.swift in Sources */,
|
FDA1AF8F21484A26001AE720 /* Obscurable.swift in Sources */,
|
||||||
FDA1AF91214871B5001AE720 /* MSCardTransitionAnimator.swift in Sources */,
|
FDA1AF91214871B5001AE720 /* MSCardTransitionAnimator.swift in Sources */,
|
||||||
A5CEC16F20D98F340016922A /* Fonts.swift in Sources */,
|
A5CEC16F20D98F340016922A /* Fonts.swift in Sources */,
|
||||||
|
B487EA7321911FD4005E90B3 /* MSBadgeView.swift in Sources */,
|
||||||
FD599D0C2134AB1E008845EE /* MSCalendarViewDataSource.swift in Sources */,
|
FD599D0C2134AB1E008845EE /* MSCalendarViewDataSource.swift in Sources */,
|
||||||
A5B87B04211E22B70038C37C /* MSDimmingView.swift in Sources */,
|
A5B87B04211E22B70038C37C /* MSDimmingView.swift in Sources */,
|
||||||
A5961F9F218A256B00E2A506 /* MSPopupMenuItem.swift in Sources */,
|
A5961F9F218A256B00E2A506 /* MSPopupMenuItem.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
// MARK: MSBadgeBaseViewDelegate
|
||||||
|
|
||||||
|
@objc public protocol MSBadgeBaseViewDelegate {
|
||||||
|
func didSelectBadge(_ badge: MSBadgeBaseView)
|
||||||
|
func didTapSelectedBadge(_ badge: MSBadgeBaseView)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSBadgeBaseViewDataSource
|
||||||
|
|
||||||
|
open class MSBadgeBaseViewDataSource: NSObject {
|
||||||
|
@objc open private(set) var text: String
|
||||||
|
|
||||||
|
@objc public init(text: String) {
|
||||||
|
self.text = text
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSBadgeBaseView
|
||||||
|
|
||||||
|
/**
|
||||||
|
`MSBadgeBaseView` is the base view used in `MSBadgeView` as a "badge".
|
||||||
|
It defines the needed interface for views handled by MSBadgeView such as:
|
||||||
|
- Colored background
|
||||||
|
- Selection logic
|
||||||
|
*/
|
||||||
|
open class MSBadgeBaseView: UIView {
|
||||||
|
private struct Constants {
|
||||||
|
static let defaultMinWidth: CGFloat = 25
|
||||||
|
static let backgroundViewCornerRadius: CGFloat = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
open class var defaultHeight: CGFloat {
|
||||||
|
assertionFailure("MSBadgeBaseView defaultHeight method must be overridden")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc open var dataSource: MSBadgeBaseViewDataSource? {
|
||||||
|
didSet {
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open weak var delegate: MSBadgeBaseViewDelegate?
|
||||||
|
|
||||||
|
open var isEnabled: Bool = true {
|
||||||
|
didSet {
|
||||||
|
updateBackgroundColor()
|
||||||
|
accessibilityHint = nil
|
||||||
|
isUserInteractionEnabled = isEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var isSelected: Bool = false {
|
||||||
|
didSet {
|
||||||
|
updateBackgroundColor()
|
||||||
|
updateAccessibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var minWidth: CGFloat = Constants.defaultMinWidth {
|
||||||
|
didSet {
|
||||||
|
setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var intrinsicContentSize: CGSize {
|
||||||
|
return sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal var badgeBackgroundColor: UIColor = MSColors.Badge.background {
|
||||||
|
didSet {
|
||||||
|
updateBackgroundColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal var badgeSelectedBackgroundColor: UIColor = MSColors.Badge.backgroundSelected {
|
||||||
|
didSet {
|
||||||
|
updateBackgroundColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal var badgeDisabledBackgroundColor: UIColor = MSColors.Badge.backgroundDisabled {
|
||||||
|
didSet {
|
||||||
|
updateBackgroundColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let backgroundView = UIView()
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
super.init(frame: .zero)
|
||||||
|
|
||||||
|
backgroundView.layer.cornerRadius = Constants.backgroundViewCornerRadius
|
||||||
|
addSubview(backgroundView)
|
||||||
|
updateBackgroundColor()
|
||||||
|
|
||||||
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(badgeTapped))
|
||||||
|
addGestureRecognizer(tapGesture)
|
||||||
|
isUserInteractionEnabled = true
|
||||||
|
|
||||||
|
accessibilityTraits = UIAccessibilityTraitButton
|
||||||
|
updateAccessibility()
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
backgroundView.frame = bounds
|
||||||
|
}
|
||||||
|
|
||||||
|
open func reload() { }
|
||||||
|
|
||||||
|
open override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||||
|
assertionFailure("MSBadgeBaseView sizeThatFits method must be overridden")
|
||||||
|
return .zero
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateAccessibility() {
|
||||||
|
if isSelected {
|
||||||
|
accessibilityValue = "Accessibility.Selected.Value".localized
|
||||||
|
accessibilityHint = "Accessibility.Selected.Hint".localized
|
||||||
|
} else {
|
||||||
|
accessibilityValue = nil
|
||||||
|
accessibilityHint = "Accessibility.Select.Hint".localized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateBackgroundColor() {
|
||||||
|
if !isEnabled {
|
||||||
|
backgroundView.backgroundColor = badgeDisabledBackgroundColor
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backgroundView.backgroundColor = isSelected ? badgeSelectedBackgroundColor : badgeBackgroundColor
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func badgeTapped() {
|
||||||
|
if isSelected {
|
||||||
|
delegate?.didTapSelectedBadge(self)
|
||||||
|
} else {
|
||||||
|
isSelected = true
|
||||||
|
delegate?.didSelectBadge(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
// MARK: MSBadgeViewStyle
|
||||||
|
|
||||||
|
@objc public enum MSBadgeViewStyle: Int {
|
||||||
|
case `default`
|
||||||
|
case warning
|
||||||
|
case error
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSBadgeViewDataSource
|
||||||
|
|
||||||
|
open class MSBadgeViewDataSource: MSBadgeBaseViewDataSource {
|
||||||
|
public var style: MSBadgeViewStyle
|
||||||
|
|
||||||
|
@objc public init(text: String, style: MSBadgeViewStyle) {
|
||||||
|
self.style = style
|
||||||
|
super.init(text: text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSBadgeView
|
||||||
|
|
||||||
|
/**
|
||||||
|
`MSBadgeView` is used to present text with a colored background supplied by `MSBadgeBaseView` in the form of a "badge". It is used in `MSBadgeListField` to represent a selected item from `MSPersonaListView`.
|
||||||
|
|
||||||
|
`MSBadgeView` can be selected with a tap gesture and tapped again after entering a selected state for the purpose of displaying more details about the entity represented by the selected badge.
|
||||||
|
*/
|
||||||
|
open class MSBadgeView: MSBadgeBaseView {
|
||||||
|
private struct Constants {
|
||||||
|
static let labelFont: UIFont = MSFonts.body
|
||||||
|
static let paddingHorizontal: CGFloat = 5
|
||||||
|
static let paddingVertical: CGFloat = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
open override class var defaultHeight: CGFloat {
|
||||||
|
return Constants.paddingVertical + Constants.labelFont.deviceLineHeight + Constants.paddingVertical
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func backgroundColor(for style: MSBadgeViewStyle, selected: Bool, enabled: Bool) -> UIColor {
|
||||||
|
switch style {
|
||||||
|
case .default:
|
||||||
|
if !enabled {
|
||||||
|
return MSColors.Badge.backgroundDisabled
|
||||||
|
} else if selected {
|
||||||
|
return MSColors.Badge.backgroundSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.background
|
||||||
|
}
|
||||||
|
case .warning:
|
||||||
|
if selected {
|
||||||
|
return MSColors.Badge.backgroundWarningSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.backgroundWarning
|
||||||
|
}
|
||||||
|
case .error:
|
||||||
|
if selected {
|
||||||
|
return MSColors.Badge.backgroundErrorSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.backgroundError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func textColor(for style: MSBadgeViewStyle, selected: Bool, enabled: Bool) -> UIColor {
|
||||||
|
switch style {
|
||||||
|
case .default:
|
||||||
|
if !enabled {
|
||||||
|
return MSColors.Badge.textDisabled
|
||||||
|
} else if selected {
|
||||||
|
return MSColors.Badge.textSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.text
|
||||||
|
}
|
||||||
|
case .warning:
|
||||||
|
if selected {
|
||||||
|
return MSColors.Badge.textWarningSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.textWarning
|
||||||
|
}
|
||||||
|
case .error:
|
||||||
|
if selected {
|
||||||
|
return MSColors.Badge.textErrorSelected
|
||||||
|
} else {
|
||||||
|
return MSColors.Badge.textError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open var badgeViewDataSource: MSBadgeViewDataSource? { return dataSource as? MSBadgeViewDataSource }
|
||||||
|
|
||||||
|
open override var isEnabled: Bool {
|
||||||
|
didSet {
|
||||||
|
updateLabelTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var isSelected: Bool {
|
||||||
|
didSet {
|
||||||
|
updateLabelTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var style: MSBadgeViewStyle = .default {
|
||||||
|
didSet {
|
||||||
|
badgeBackgroundColor = MSBadgeView.backgroundColor(for: style, selected: false, enabled: true)
|
||||||
|
badgeSelectedBackgroundColor = MSBadgeView.backgroundColor(for: style, selected: true, enabled: true)
|
||||||
|
badgeDisabledBackgroundColor = MSBadgeView.backgroundColor(for: style, selected: false, enabled: false)
|
||||||
|
|
||||||
|
textColor = MSBadgeView.textColor(for: style, selected: false, enabled: true)
|
||||||
|
selectedTextColor = MSBadgeView.textColor(for: style, selected: true, enabled: true)
|
||||||
|
disabledTextColor = MSBadgeView.textColor(for: style, selected: false, enabled: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var textColor: UIColor = MSColors.Badge.text {
|
||||||
|
didSet {
|
||||||
|
updateLabelTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var selectedTextColor: UIColor = MSColors.Badge.textSelected {
|
||||||
|
didSet {
|
||||||
|
updateLabelTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var disabledTextColor: UIColor = MSColors.Badge.textDisabled {
|
||||||
|
didSet {
|
||||||
|
updateLabelTextColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let label = UILabel()
|
||||||
|
|
||||||
|
public override init() {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
label.font = Constants.labelFont
|
||||||
|
label.lineBreakMode = .byTruncatingMiddle
|
||||||
|
label.textAlignment = .center
|
||||||
|
label.backgroundColor = .clear
|
||||||
|
addSubview(label)
|
||||||
|
|
||||||
|
updateLabelTextColor()
|
||||||
|
isAccessibilityElement = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func reload() {
|
||||||
|
label.text = dataSource?.text
|
||||||
|
style = badgeViewDataSource?.style ?? .default
|
||||||
|
setNeedsLayout()
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
let labelHeight = label.font.deviceLineHeight
|
||||||
|
let labelSize = label.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
let fittingLabelWidth = UIScreen.main.roundToDevicePixels(labelSize.width)
|
||||||
|
let minLabelWidth = minWidth - 2 * Constants.paddingHorizontal
|
||||||
|
let maxLabelWidth = width - 2 * Constants.paddingHorizontal
|
||||||
|
let labelWidth = max(minLabelWidth, min(maxLabelWidth, fittingLabelWidth))
|
||||||
|
label.frame = CGRect(x: Constants.paddingHorizontal, y: Constants.paddingVertical, width: labelWidth, height: labelHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func sizeThatFits(_ size: CGSize) -> CGSize {
|
||||||
|
let labelSize = label.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
|
||||||
|
let fittingLabelWidth = UIScreen.main.roundToDevicePixels(labelSize.width)
|
||||||
|
let horizontalPadding = 2 * Constants.paddingHorizontal
|
||||||
|
let labelWidth = max(min(fittingLabelWidth, size.width), minWidth - horizontalPadding)
|
||||||
|
return CGSize(width: labelWidth + horizontalPadding, height: MSBadgeView.defaultHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLabelTextColor() {
|
||||||
|
if !isEnabled {
|
||||||
|
label.textColor = disabledTextColor
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
label.textColor = isSelected ? selectedTextColor : textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Accessibility
|
||||||
|
|
||||||
|
open override var accessibilityLabel: String? { get { return label.text } set { } }
|
||||||
|
}
|
|
@ -37,9 +37,14 @@ public struct MSColors {
|
||||||
public static let black: UIColor = #colorLiteral(red: 0.1333333333, green: 0.1333333333, blue: 0.1333333333, alpha: 1)
|
public static let black: UIColor = #colorLiteral(red: 0.1333333333, green: 0.1333333333, blue: 0.1333333333, alpha: 1)
|
||||||
|
|
||||||
/// #E8484C
|
/// #E8484C
|
||||||
public static let warning: UIColor = #colorLiteral(red: 0.9098039216, green: 0.2823529412, blue: 0.2980392157, alpha: 1)
|
public static let error: UIColor = #colorLiteral(red: 0.9098039216, green: 0.2823529412, blue: 0.2980392157, alpha: 1)
|
||||||
/// #FFF3F4
|
/// #FFF3F4
|
||||||
public static let lightWarning: UIColor = #colorLiteral(red: 1, green: 0.9529411765, blue: 0.9568627451, alpha: 1)
|
public static let lightError: UIColor = #colorLiteral(red: 1, green: 0.9529411765, blue: 0.9568627451, alpha: 1)
|
||||||
|
|
||||||
|
/// #574305
|
||||||
|
public static let warning: UIColor = #colorLiteral(red: 0.3411764706, green: 0.262745098, blue: 0.01960784314, alpha: 1)
|
||||||
|
/// #E2DDCC
|
||||||
|
public static let lightWarning: UIColor = #colorLiteral(red: 0.8862745098, green: 0.8666666667, blue: 0.8, alpha: 1)
|
||||||
|
|
||||||
// MARK: Avatar background colors
|
// MARK: Avatar background colors
|
||||||
|
|
||||||
|
@ -79,14 +84,31 @@ public struct MSColors {
|
||||||
public struct Action {
|
public struct Action {
|
||||||
public static let text: UIColor = primary
|
public static let text: UIColor = primary
|
||||||
public static let textHighlighted: UIColor = primary.withAlphaComponent(0.4)
|
public static let textHighlighted: UIColor = primary.withAlphaComponent(0.4)
|
||||||
public static let textDestructive: UIColor = warning
|
public static let textDestructive: UIColor = error
|
||||||
public static let textDestructiveHighlighted: UIColor = warning.withAlphaComponent(0.4)
|
public static let textDestructiveHighlighted: UIColor = error.withAlphaComponent(0.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Avatar {
|
public struct Avatar {
|
||||||
public static let text: UIColor = white
|
public static let text: UIColor = white
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Badge {
|
||||||
|
public static let background: UIColor = primary.withAlphaComponent(0.24)
|
||||||
|
public static let backgroundDisabled: UIColor = backgroundLightGray
|
||||||
|
public static let backgroundError: UIColor = lightError
|
||||||
|
public static let backgroundErrorSelected: UIColor = error
|
||||||
|
public static let backgroundSelected: UIColor = primary
|
||||||
|
public static let backgroundWarning: UIColor = lightWarning
|
||||||
|
public static let backgroundWarningSelected: UIColor = warning
|
||||||
|
public static let text: UIColor = primary
|
||||||
|
public static let textDisabled: UIColor = darkGray
|
||||||
|
public static let textError: UIColor = error
|
||||||
|
public static let textErrorSelected: UIColor = lightError
|
||||||
|
public static let textSelected: UIColor = white
|
||||||
|
public static let textWarning: UIColor = warning
|
||||||
|
public static let textWarningSelected: UIColor = lightWarning
|
||||||
|
}
|
||||||
|
|
||||||
public struct CalendarView {
|
public struct CalendarView {
|
||||||
public struct TodayCell {
|
public struct TodayCell {
|
||||||
public static let background: UIColor = white
|
public static let background: UIColor = white
|
||||||
|
@ -138,6 +160,7 @@ public enum MSTextColorStyle: Int {
|
||||||
case secondary
|
case secondary
|
||||||
case white
|
case white
|
||||||
case primary
|
case primary
|
||||||
|
case error
|
||||||
case warning
|
case warning
|
||||||
|
|
||||||
// TODO: Replace with conformance to CaseIterable after switch to Swift 4.2
|
// TODO: Replace with conformance to CaseIterable after switch to Swift 4.2
|
||||||
|
@ -153,6 +176,8 @@ public enum MSTextColorStyle: Int {
|
||||||
return MSColors.white
|
return MSColors.white
|
||||||
case .primary:
|
case .primary:
|
||||||
return MSColors.primary
|
return MSColors.primary
|
||||||
|
case .error:
|
||||||
|
return MSColors.error
|
||||||
case .warning:
|
case .warning:
|
||||||
return MSColors.warning
|
return MSColors.warning
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,6 +223,7 @@ open class MSAvatarView: UIView {
|
||||||
|
|
||||||
// MARK: Accessibility
|
// MARK: Accessibility
|
||||||
|
|
||||||
|
open override var isAccessibilityElement: Bool { get { return true } set { } }
|
||||||
open override var accessibilityLabel: String? { get { return name ?? email } set { } }
|
open override var accessibilityLabel: String? { get { return name ?? email } set { } }
|
||||||
open override var accessibilityTraits: UIAccessibilityTraits { get { return UIAccessibilityTraitImage } set { } }
|
open override var accessibilityTraits: UIAccessibilityTraits { get { return UIAccessibilityTraitImage } set { } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ open class MSPersonaCell: UITableViewCell {
|
||||||
setupCellBackgroundColors()
|
setupCellBackgroundColors()
|
||||||
nameLabel.lineBreakMode = .byTruncatingTail
|
nameLabel.lineBreakMode = .byTruncatingTail
|
||||||
subtitleLabel.lineBreakMode = .byTruncatingTail
|
subtitleLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
avatarView.accessibilityElementsHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(coder aDecoder: NSCoder) {
|
public required init(coder aDecoder: NSCoder) {
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
"Accessibility.Alert" = "Alert";
|
"Accessibility.Alert" = "Alert";
|
||||||
"Accessibility.Dismiss.Label" = "Dismiss";
|
"Accessibility.Dismiss.Label" = "Dismiss";
|
||||||
"Accessibility.Dismiss.Hint" = "Double tap to dismiss";
|
"Accessibility.Dismiss.Hint" = "Double tap to dismiss";
|
||||||
|
"Accessibility.Select.Hint" = "Double tap to select";
|
||||||
|
"Accessibility.Selected.Hint" = "Double tap to see details";
|
||||||
|
"Accessibility.Selected.Value" = "Selected";
|
||||||
|
|
||||||
/* Accessibility label for the upper calendar date picker view. */
|
/* Accessibility label for the upper calendar date picker view. */
|
||||||
"Accessibility.Calendar.Label" = "Calendar";
|
"Accessibility.Calendar.Label" = "Calendar";
|
||||||
|
@ -23,5 +26,3 @@
|
||||||
|
|
||||||
// MSPersonaListView
|
// MSPersonaListView
|
||||||
"MSPersonaListView.SearchDirectory" = "Search Directory";
|
"MSPersonaListView.SearchDirectory" = "Search Directory";
|
||||||
"MSPersonaListView.SingleResult" = "result found from directory";
|
|
||||||
"MSPersonaListView.MultipleResults" = "results found from directory";
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче