Merged PR 200173: Popup Menu implementation based on Drawer
I used our Drawer controller to implement Popup Menu. In Outlook presentation and content are coupled together for each of Popup Menu and Action Sheet. We separated presentation (Drawer) from content. This work builds on existing presentation code (Drawer) and adds content as Popup Menu. Public API is very similar to Outlook with some cleaning done. This was my FHL project :-) Related work items: #653496
До Ширина: | Высота: | Размер: 276 KiB После Ширина: | Высота: | Размер: 276 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Las Vegas.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Las Vegas@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Las Vegas.imageset/Las Vegas@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 10 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Montreal.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Montreal@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Montreal.imageset/Montreal@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 13 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Phoenix.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Phoenix@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Phoenix.imageset/Phoenix@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 6.9 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/San Francisco.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "San Francisco@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/San Francisco.imageset/San Francisco@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 7.7 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Seattle.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Seattle@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Seattle.imageset/Seattle@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 11 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Toronto.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Toronto@3x.jpg",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Toronto.imageset/Toronto@3x.jpg
поставляемый
Normal file
После Ширина: | Высота: | Размер: 3.4 KiB |
21
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Vancouver.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "Vancouver@3x.png",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/Cities/Vancouver.imageset/Vancouver@3x.png
поставляемый
Normal file
После Ширина: | Высота: | Размер: 14 KiB |
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/agenda-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "agenda-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/agenda-25x25.imageset/agenda-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/attach-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "attach-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/attach-25x25.imageset/attach-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/day-view-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "day-view-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/day-view-25x25.imageset/day-view-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/flag-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "flag-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/flag-25x25.imageset/flag-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/mail-unread-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "mail-unread-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/mail-unread-25x25.imageset/mail-unread-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/month-view-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "month-view-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/month-view-25x25.imageset/month-view-25x25.pdf
поставляемый
Normal file
12
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/week-view-25x25.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "week-view-25x25.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|
Двоичные данные
OfficeUIFabric.Demo/OfficeUIFabric.Demo/Assets.xcassets/PopupMenu/week-view-25x25.imageset/week-view-25x25.pdf
поставляемый
Normal file
|
@ -11,5 +11,6 @@ let demos: [(title: String, controllerClass: UIViewController.Type)] = [
|
||||||
("MSDatePicker", MSDatePickerDemoController.self),
|
("MSDatePicker", MSDatePickerDemoController.self),
|
||||||
("MSDrawerController", MSDrawerDemoController.self),
|
("MSDrawerController", MSDrawerDemoController.self),
|
||||||
("MSLabel", MSLabelDemoController.self),
|
("MSLabel", MSLabelDemoController.self),
|
||||||
("MSPersonaListView", MSPersonaListViewDemoController.self)
|
("MSPersonaListView", MSPersonaListViewDemoController.self),
|
||||||
|
("MSPopupMenuController", MSPopupMenuDemoController.self)
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,12 +5,40 @@
|
||||||
import OfficeUIFabric
|
import OfficeUIFabric
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
let samplePersonas: [MSPersonaData] = [
|
||||||
|
MSPersonaData(name: "Kat Larrson", email: "kat.larrson@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_kat_larsson")),
|
||||||
|
MSPersonaData(name: "Kristin Patterson", email: "kristin.patterson@contoso.com", subtitle: "Software Engineer"),
|
||||||
|
MSPersonaData(name: "Ashley McCarthy", avatarImage: UIImage(named: "avatar_ashley_mccarthy")),
|
||||||
|
MSPersonaData(name: "Carole Poland", email: "carole.poland@contoso.com", subtitle: "Software Engineer"),
|
||||||
|
MSPersonaData(name: "Allan Munger", email: "allan.munger@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_allan_munger")),
|
||||||
|
MSPersonaData(name: "Amanda Brady", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_amanda_brady")),
|
||||||
|
MSPersonaData(name: "Kevin Sturgis", email: "kevin.sturgis@contoso.com", subtitle: "Software Engineeer"),
|
||||||
|
MSPersonaData(name: "Lydia Bauer", email: "lydia.bauer@contoso.com", avatarImage: UIImage(named: "avatar_lydia_bauer")),
|
||||||
|
MSPersonaData(name: "Robin Counts", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_robin_counts")),
|
||||||
|
MSPersonaData(name: "Tim Deboer", email: "tim.deboer@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_tim_deboer")),
|
||||||
|
MSPersonaData(email: "wanda.howard@contoso.com", subtitle: "Director"),
|
||||||
|
MSPersonaData(name: "Daisy Phillips", email: "daisy.phillips@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_daisy_phillips")),
|
||||||
|
MSPersonaData(name: "Katri Ahokas", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_katri_ahokas")),
|
||||||
|
MSPersonaData(name: "Colin Ballinger", email: "colin.ballinger@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_colin_ballinger")),
|
||||||
|
MSPersonaData(name: "Mona Kane", email: "mona.kane@contoso.com", subtitle: "Designer"),
|
||||||
|
MSPersonaData(name: "Elvia Atkins", email: "elvia.atkins@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_elvia_atkins")),
|
||||||
|
MSPersonaData(name: "Johnie McConnell", subtitle: "Designer", avatarImage: UIImage(named: "avatar_johnie_mcconnell")),
|
||||||
|
MSPersonaData(name: "Charlotte Waltsson", email: "charlotte.waltsson@contoso.com", subtitle: "Software Engineer"),
|
||||||
|
MSPersonaData(name: "Mauricio August", email: "mauricio.august@contoso.com", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_mauricio_august")),
|
||||||
|
MSPersonaData(name: "Robert Tolbert", email: "robert.tolbert@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_robert_tolbert")),
|
||||||
|
MSPersonaData(name: "Isaac Fielder", subtitle: "Designer", avatarImage: UIImage(named: "avatar_isaac_fielder")),
|
||||||
|
MSPersonaData(name: "Elliot Woodward", subtitle: "Designer"),
|
||||||
|
MSPersonaData(email: "carlos.slattery@contoso.com", subtitle: "Software Engineer"),
|
||||||
|
MSPersonaData(name: "Henry Brill", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_henry_brill")),
|
||||||
|
MSPersonaData(name: "Cecil Folk", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_cecil_folk"))
|
||||||
|
]
|
||||||
|
|
||||||
class MSPersonaListViewDemoController: DemoController {
|
class MSPersonaListViewDemoController: DemoController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
let personaListView = MSPersonaListView()
|
let personaListView = MSPersonaListView()
|
||||||
personaListView.personaList = personas()
|
personaListView.personaList = samplePersonas
|
||||||
personaListView.showsSearchDirectoryButton = true
|
personaListView.showsSearchDirectoryButton = true
|
||||||
personaListView.searchDirectoryDelegate = self
|
personaListView.searchDirectoryDelegate = self
|
||||||
personaListView.onPersonaSelected = { [unowned self] persona in
|
personaListView.onPersonaSelected = { [unowned self] persona in
|
||||||
|
@ -23,36 +51,6 @@ class MSPersonaListViewDemoController: DemoController {
|
||||||
view.addSubview(personaListView)
|
view.addSubview(personaListView)
|
||||||
personaListView.fitIntoSuperview()
|
personaListView.fitIntoSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func personas() -> [MSPersonaData] {
|
|
||||||
return [
|
|
||||||
MSPersonaData(name: "Kat Larrson", email: "kat.larrson@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_kat_larsson")),
|
|
||||||
MSPersonaData(name: "Kristin Patterson", email: "kristin.patterson@contoso.com", subtitle: "Software Engineer"),
|
|
||||||
MSPersonaData(name: "Ashley McCarthy", avatarImage: UIImage(named: "avatar_ashley_mccarthy")),
|
|
||||||
MSPersonaData(name: "Carole Poland", email: "carole.poland@contoso.com", subtitle: "Software Engineer"),
|
|
||||||
MSPersonaData(name: "Allan Munger", email: "allan.munger@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_allan_munger")),
|
|
||||||
MSPersonaData(name: "Amanda Brady", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_amanda_brady")),
|
|
||||||
MSPersonaData(name: "Kevin Sturgis", email: "kevin.sturgis@contoso.com", subtitle: "Software Engineeer"),
|
|
||||||
MSPersonaData(name: "Lydia Bauer", email: "lydia.bauer@contoso.com", avatarImage: UIImage(named: "avatar_lydia_bauer")),
|
|
||||||
MSPersonaData(name: "Robin Counts", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_robin_counts")),
|
|
||||||
MSPersonaData(name: "Tim Deboer", email: "tim.deboer@contoso.com", subtitle: "Designer", avatarImage: UIImage(named: "avatar_tim_deboer")),
|
|
||||||
MSPersonaData(email: "wanda.howard@contoso.com", subtitle: "Director"),
|
|
||||||
MSPersonaData(name: "Daisy Phillips", email: "daisy.phillips@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_daisy_phillips")),
|
|
||||||
MSPersonaData(name: "Katri Ahokas", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_katri_ahokas")),
|
|
||||||
MSPersonaData(name: "Colin Ballinger", email: "colin.ballinger@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_colin_ballinger")),
|
|
||||||
MSPersonaData(name: "Mona Kane", email: "mona.kane@contoso.com", subtitle: "Designer"),
|
|
||||||
MSPersonaData(name: "Elvia Atkins", email: "elvia.atkins@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_elvia_atkins")),
|
|
||||||
MSPersonaData(name: "Johnie McConnell", subtitle: "Designer", avatarImage: UIImage(named: "avatar_johnie_mcconnell")),
|
|
||||||
MSPersonaData(name: "Charlotte Waltsson", email: "charlotte.waltsson@contoso.com", subtitle: "Software Engineer"),
|
|
||||||
MSPersonaData(name: "Mauricio August", email: "mauricio.august@contoso.com", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_mauricio_august")),
|
|
||||||
MSPersonaData(name: "Robert Tolbert", email: "robert.tolbert@contoso.com", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_robert_tolbert")),
|
|
||||||
MSPersonaData(name: "Isaac Fielder", subtitle: "Designer", avatarImage: UIImage(named: "avatar_isaac_fielder")),
|
|
||||||
MSPersonaData(name: "Elliot Woodward", subtitle: "Designer"),
|
|
||||||
MSPersonaData(email: "carlos.slattery@contoso.com", subtitle: "Software Engineer"),
|
|
||||||
MSPersonaData(name: "Henry Brill", subtitle: "Software Engineer", avatarImage: UIImage(named: "avatar_henry_brill")),
|
|
||||||
MSPersonaData(name: "Cecil Folk", subtitle: "Program Manager", avatarImage: UIImage(named: "avatar_cecil_folk"))
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - MSPersonaListViewDemoController: MSPersonaListViewSearchDirectoryDelegate
|
// MARK: - MSPersonaListViewDemoController: MSPersonaListViewSearchDirectoryDelegate
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import OfficeUIFabric
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class MSPopupMenuDemoController: DemoController {
|
||||||
|
private enum CalendarLayout {
|
||||||
|
case agenda
|
||||||
|
case day
|
||||||
|
case threeDay
|
||||||
|
case week
|
||||||
|
case month
|
||||||
|
}
|
||||||
|
|
||||||
|
private var calendarLayout: CalendarLayout = .agenda
|
||||||
|
private var cityIndexPath: IndexPath? = IndexPath(item: 2, section: 1)
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Show", style: .plain, target: self, action: #selector(topBarButtonTapped))
|
||||||
|
|
||||||
|
toolbarItems = [
|
||||||
|
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
|
||||||
|
UIBarButtonItem(title: "Show with selection", style: .plain, target: self, action: #selector(bottomBarButtonTapped)),
|
||||||
|
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
|
||||||
|
]
|
||||||
|
navigationController?.isToolbarHidden = false
|
||||||
|
|
||||||
|
container.addArrangedSubview(createButton(title: "Show with sections", action: #selector(showTopMenuWithSectionsButtonTapped)))
|
||||||
|
container.addArrangedSubview(createButton(title: "Show with scrollable items and no icons", action: #selector(showTopMenuWithScrollableItemsButtonTapped)))
|
||||||
|
container.addArrangedSubview(UIView())
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func topBarButtonTapped(sender: UIBarButtonItem) {
|
||||||
|
let controller = MSPopupMenuController(barButtonItem: sender, presentationDirection: .down)
|
||||||
|
|
||||||
|
controller.addItems([
|
||||||
|
MSPopupMenuItem(imageName: "mail-unread-25x25", title: "Unread"),
|
||||||
|
MSPopupMenuItem(imageName: "flag-25x25", title: "Flagged"),
|
||||||
|
MSPopupMenuItem(imageName: "attach-25x25", title: "Attachments")
|
||||||
|
])
|
||||||
|
|
||||||
|
let originalTitle = sender.title
|
||||||
|
sender.title = "Shown"
|
||||||
|
controller.onDismiss = {
|
||||||
|
sender.title = originalTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
present(controller, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func bottomBarButtonTapped(sender: UIBarButtonItem) {
|
||||||
|
var origin: CGFloat = -1
|
||||||
|
if let toolbar = navigationController?.toolbar {
|
||||||
|
origin = toolbar.convert(toolbar.bounds.origin, to: nil).y
|
||||||
|
}
|
||||||
|
|
||||||
|
let controller = MSPopupMenuController(barButtonItem: sender, presentationOrigin: origin, presentationDirection: .up)
|
||||||
|
|
||||||
|
controller.addItems([
|
||||||
|
MSPopupMenuItem(imageName: "agenda-25x25", title: "Agenda", isSelected: calendarLayout == .agenda, onSelected: { self.calendarLayout = .agenda }),
|
||||||
|
MSPopupMenuItem(imageName: "day-view-25x25", title: "Day", isSelected: calendarLayout == .day, onSelected: { self.calendarLayout = .day }),
|
||||||
|
MSPopupMenuItem(imageName: "week-view-25x25", title: "3-Day", isEnabled: false, isSelected: calendarLayout == .threeDay, onSelected: { self.calendarLayout = .threeDay }),
|
||||||
|
MSPopupMenuItem(title: "Week (no icon)", isSelected: calendarLayout == .week, onSelected: { self.calendarLayout = .week }),
|
||||||
|
MSPopupMenuItem(imageName: "month-view-25x25", title: "Month", isSelected: calendarLayout == .month, onSelected: { self.calendarLayout = .month })
|
||||||
|
])
|
||||||
|
|
||||||
|
let originalTitle = sender.title
|
||||||
|
sender.title = "Shown with selection"
|
||||||
|
controller.onDismiss = {
|
||||||
|
sender.title = originalTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
present(controller, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func showTopMenuWithSectionsButtonTapped(sender: UIButton) {
|
||||||
|
let controller = MSPopupMenuController(sourceView: sender, sourceRect: sender.bounds, presentationDirection: .down)
|
||||||
|
|
||||||
|
controller.addSections([
|
||||||
|
MSPopupMenuSection(title: "Canada", items: [
|
||||||
|
MSPopupMenuItem(imageName: "Montreal", generateSelectedImage: false, title: "Montréal", subtitle: "Québec"),
|
||||||
|
MSPopupMenuItem(imageName: "Toronto", generateSelectedImage: false, title: "Toronto", subtitle: "Ontario"),
|
||||||
|
MSPopupMenuItem(imageName: "Vancouver", generateSelectedImage: false, title: "Vancouver", subtitle: "British Columbia")
|
||||||
|
]),
|
||||||
|
MSPopupMenuSection(title: "United States", items: [
|
||||||
|
MSPopupMenuItem(imageName: "Las Vegas", generateSelectedImage: false, title: "Las Vegas", subtitle: "Nevada"),
|
||||||
|
MSPopupMenuItem(imageName: "Phoenix", generateSelectedImage: false, title: "Phoenix", subtitle: "Arizona"),
|
||||||
|
MSPopupMenuItem(imageName: "San Francisco", generateSelectedImage: false, title: "San Francisco", subtitle: "California"),
|
||||||
|
MSPopupMenuItem(imageName: "Seattle", generateSelectedImage: false, title: "Seattle", subtitle: "Washington")
|
||||||
|
])
|
||||||
|
])
|
||||||
|
|
||||||
|
controller.selectedItemIndexPath = cityIndexPath
|
||||||
|
controller.onDismiss = { [unowned controller] in
|
||||||
|
self.cityIndexPath = controller.selectedItemIndexPath
|
||||||
|
}
|
||||||
|
|
||||||
|
present(controller, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func showTopMenuWithScrollableItemsButtonTapped(sender: UIButton) {
|
||||||
|
let controller = MSPopupMenuController(sourceView: sender, sourceRect: sender.bounds, presentationDirection: .down)
|
||||||
|
|
||||||
|
let items = samplePersonas.map { MSPopupMenuItem(title: !$0.name.isEmpty ? $0.name : $0.email) }
|
||||||
|
controller.addItems(items)
|
||||||
|
|
||||||
|
present(controller, animated: true)
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,12 @@
|
||||||
A559BB7F212B6FA40055E107 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A559BB81212B6FA40055E107 /* Localizable.strings */; };
|
A559BB7F212B6FA40055E107 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A559BB81212B6FA40055E107 /* Localizable.strings */; };
|
||||||
A559BB83212B7D870055E107 /* OfficeUIFabric.swift in Sources */ = {isa = PBXBuildFile; fileRef = A559BB82212B7D870055E107 /* OfficeUIFabric.swift */; };
|
A559BB83212B7D870055E107 /* OfficeUIFabric.swift in Sources */ = {isa = PBXBuildFile; fileRef = A559BB82212B7D870055E107 /* OfficeUIFabric.swift */; };
|
||||||
A589F854211BA03200471C23 /* MSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A589F853211BA03200471C23 /* MSLabel.swift */; };
|
A589F854211BA03200471C23 /* MSLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A589F853211BA03200471C23 /* MSLabel.swift */; };
|
||||||
|
A5961F9D218A254D00E2A506 /* MSPopupMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961F9C218A254D00E2A506 /* MSPopupMenuController.swift */; };
|
||||||
|
A5961F9F218A256B00E2A506 /* MSPopupMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961F9E218A256B00E2A506 /* MSPopupMenuItem.swift */; };
|
||||||
|
A5961FA1218A25C400E2A506 /* MSPopupMenuSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961FA0218A25C400E2A506 /* MSPopupMenuSection.swift */; };
|
||||||
|
A5961FA3218A25D100E2A506 /* MSPopupMenuItemCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961FA2218A25D100E2A506 /* MSPopupMenuItemCell.swift */; };
|
||||||
|
A5961FA5218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961FA4218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift */; };
|
||||||
|
A5961FA7218A2E4500E2A506 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5961FA6218A2E4500E2A506 /* UIImage+Extensions.swift */; };
|
||||||
A5B87AEF211BCA710038C37C /* UIFontDescriptor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */; };
|
A5B87AEF211BCA710038C37C /* UIFontDescriptor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */; };
|
||||||
A5B87AF1211BD4380038C37C /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */; };
|
A5B87AF1211BD4380038C37C /* UIFont+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */; };
|
||||||
A5B87AF6211E16370038C37C /* MSDrawerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AF3211E16360038C37C /* MSDrawerController.swift */; };
|
A5B87AF6211E16370038C37C /* MSDrawerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B87AF3211E16360038C37C /* MSDrawerController.swift */; };
|
||||||
|
@ -90,6 +96,12 @@
|
||||||
A559BB80212B6FA40055E107 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
A559BB80212B6FA40055E107 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
A559BB82212B7D870055E107 /* OfficeUIFabric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfficeUIFabric.swift; sourceTree = "<group>"; };
|
A559BB82212B7D870055E107 /* OfficeUIFabric.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfficeUIFabric.swift; sourceTree = "<group>"; };
|
||||||
A589F853211BA03200471C23 /* MSLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSLabel.swift; sourceTree = "<group>"; };
|
A589F853211BA03200471C23 /* MSLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSLabel.swift; sourceTree = "<group>"; };
|
||||||
|
A5961F9C218A254D00E2A506 /* MSPopupMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPopupMenuController.swift; sourceTree = "<group>"; };
|
||||||
|
A5961F9E218A256B00E2A506 /* MSPopupMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPopupMenuItem.swift; sourceTree = "<group>"; };
|
||||||
|
A5961FA0218A25C400E2A506 /* MSPopupMenuSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPopupMenuSection.swift; sourceTree = "<group>"; };
|
||||||
|
A5961FA2218A25D100E2A506 /* MSPopupMenuItemCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPopupMenuItemCell.swift; sourceTree = "<group>"; };
|
||||||
|
A5961FA4218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSPopupMenuSectionHeaderView.swift; sourceTree = "<group>"; };
|
||||||
|
A5961FA6218A2E4500E2A506 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
|
||||||
A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFontDescriptor+Extension.swift"; sourceTree = "<group>"; };
|
A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFontDescriptor+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Extension.swift"; sourceTree = "<group>"; };
|
A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Extension.swift"; sourceTree = "<group>"; };
|
||||||
A5B87AF3211E16360038C37C /* MSDrawerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDrawerController.swift; sourceTree = "<group>"; };
|
A5B87AF3211E16360038C37C /* MSDrawerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MSDrawerController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -172,6 +184,18 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
A5961F9B218A251E00E2A506 /* Popup Menu */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
A5961F9C218A254D00E2A506 /* MSPopupMenuController.swift */,
|
||||||
|
A5961F9E218A256B00E2A506 /* MSPopupMenuItem.swift */,
|
||||||
|
A5961FA0218A25C400E2A506 /* MSPopupMenuSection.swift */,
|
||||||
|
A5961FA2218A25D100E2A506 /* MSPopupMenuItemCell.swift */,
|
||||||
|
A5961FA4218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift */,
|
||||||
|
);
|
||||||
|
path = "Popup Menu";
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
A5B87AED211BCA4A0038C37C /* Extensions */ = {
|
A5B87AED211BCA4A0038C37C /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -185,6 +209,7 @@
|
||||||
A5B87AFF211E1B700038C37C /* UIDevice+Extension.swift */,
|
A5B87AFF211E1B700038C37C /* UIDevice+Extension.swift */,
|
||||||
A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */,
|
A5B87AF0211BD4380038C37C /* UIFont+Extension.swift */,
|
||||||
A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */,
|
A5B87AEE211BCA710038C37C /* UIFontDescriptor+Extension.swift */,
|
||||||
|
A5961FA6218A2E4500E2A506 /* UIImage+Extensions.swift */,
|
||||||
FD5BBE3E214C68F8008964B4 /* UINavigationBar+Extensions.swift */,
|
FD5BBE3E214C68F8008964B4 /* UINavigationBar+Extensions.swift */,
|
||||||
A5B87B01211E20B50038C37C /* UIScreen+Extension.swift */,
|
A5B87B01211E20B50038C37C /* UIScreen+Extension.swift */,
|
||||||
B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */,
|
B444D6B02181403C0002B4D4 /* UITableViewCell+Extension.swift */,
|
||||||
|
@ -232,6 +257,7 @@
|
||||||
A5B87AF2211E13D00038C37C /* Drawer */,
|
A5B87AF2211E13D00038C37C /* Drawer */,
|
||||||
A5B87AED211BCA4A0038C37C /* Extensions */,
|
A5B87AED211BCA4A0038C37C /* Extensions */,
|
||||||
B426613C214731AC00E25423 /* People Picker */,
|
B426613C214731AC00E25423 /* People Picker */,
|
||||||
|
A5961F9B218A251E00E2A506 /* Popup Menu */,
|
||||||
FD7254EE2147382D002F4069 /* Presenters */,
|
FD7254EE2147382D002F4069 /* Presenters */,
|
||||||
B444D6B421825B510002B4D4 /* Table View */,
|
B444D6B421825B510002B4D4 /* Table View */,
|
||||||
A5CEC17020D996120016922A /* Resources */,
|
A5CEC17020D996120016922A /* Resources */,
|
||||||
|
@ -512,10 +538,12 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
B46D3F91215056940029772C /* CharacterSet+Extension.swift in Sources */,
|
B46D3F91215056940029772C /* CharacterSet+Extension.swift in Sources */,
|
||||||
|
A5961FA3218A25D100E2A506 /* MSPopupMenuItemCell.swift in Sources */,
|
||||||
FD599D0221348439008845EE /* MSCalendarView.swift in Sources */,
|
FD599D0221348439008845EE /* MSCalendarView.swift in Sources */,
|
||||||
A559BB83212B7D870055E107 /* OfficeUIFabric.swift in Sources */,
|
A559BB83212B7D870055E107 /* OfficeUIFabric.swift in Sources */,
|
||||||
FD599D082134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift in Sources */,
|
FD599D082134AB0E008845EE /* MSCalendarViewWeekdayHeadingView.swift in Sources */,
|
||||||
FDFB8AF321361C9D0046850A /* MSCalendarViewDayMonthYearCell.swift in Sources */,
|
FDFB8AF321361C9D0046850A /* MSCalendarViewDayMonthYearCell.swift in Sources */,
|
||||||
|
A5961F9D218A254D00E2A506 /* MSPopupMenuController.swift in Sources */,
|
||||||
A5B87B00211E1B700038C37C /* UIDevice+Extension.swift in Sources */,
|
A5B87B00211E1B700038C37C /* UIDevice+Extension.swift in Sources */,
|
||||||
B4E782C521793BB900A7DFCE /* MSActivityIndicatorView.swift in Sources */,
|
B4E782C521793BB900A7DFCE /* MSActivityIndicatorView.swift in Sources */,
|
||||||
FD5BBE41214C6AF3008964B4 /* MSTwoLinesTitleView.swift in Sources */,
|
FD5BBE41214C6AF3008964B4 /* MSTwoLinesTitleView.swift in Sources */,
|
||||||
|
@ -533,6 +561,7 @@
|
||||||
B4EF53C3215AF1AB00573E8F /* MSPersona.swift in Sources */,
|
B4EF53C3215AF1AB00573E8F /* MSPersona.swift in Sources */,
|
||||||
FDFB8AEB21361C950046850A /* MSCalendarViewMonthBannerView.swift in Sources */,
|
FDFB8AEB21361C950046850A /* MSCalendarViewMonthBannerView.swift in Sources */,
|
||||||
A5B87B06211E23650038C37C /* UIView+Extensions.swift in Sources */,
|
A5B87B06211E23650038C37C /* UIView+Extensions.swift in Sources */,
|
||||||
|
A5961FA7218A2E4500E2A506 /* UIImage+Extensions.swift in Sources */,
|
||||||
B42661422148568800E25423 /* MSAvatarView.swift in Sources */,
|
B42661422148568800E25423 /* MSAvatarView.swift in Sources */,
|
||||||
FD7254E72146E946002F4069 /* MSCalendarViewDayCell.swift in Sources */,
|
FD7254E72146E946002F4069 /* MSCalendarViewDayCell.swift in Sources */,
|
||||||
A589F854211BA03200471C23 /* MSLabel.swift in Sources */,
|
A589F854211BA03200471C23 /* MSLabel.swift in Sources */,
|
||||||
|
@ -548,6 +577,7 @@
|
||||||
A5CEC16F20D98F340016922A /* Fonts.swift in Sources */,
|
A5CEC16F20D98F340016922A /* Fonts.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 */,
|
||||||
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */,
|
B4E782C321793AB200A7DFCE /* MSActivityIndicatorCell.swift in Sources */,
|
||||||
FD4F2A1E214ADBCF00C437D6 /* MSDatePicker.swift in Sources */,
|
FD4F2A1E214ADBCF00C437D6 /* MSDatePicker.swift in Sources */,
|
||||||
FD599D0A2134AB15008845EE /* MSCalendarViewLayout.swift in Sources */,
|
FD599D0A2134AB15008845EE /* MSCalendarViewLayout.swift in Sources */,
|
||||||
|
@ -556,6 +586,7 @@
|
||||||
A5CEC23120E451D00016922A /* MSButton.swift in Sources */,
|
A5CEC23120E451D00016922A /* MSButton.swift in Sources */,
|
||||||
FDF41EDB2141A23B00EC527C /* UIViewController+Extensions.swift in Sources */,
|
FDF41EDB2141A23B00EC527C /* UIViewController+Extensions.swift in Sources */,
|
||||||
FDA1AF8C21484625001AE720 /* MSBlurringView.swift in Sources */,
|
FDA1AF8C21484625001AE720 /* MSBlurringView.swift in Sources */,
|
||||||
|
A5961FA1218A25C400E2A506 /* MSPopupMenuSection.swift in Sources */,
|
||||||
A5B87AF6211E16370038C37C /* MSDrawerController.swift in Sources */,
|
A5B87AF6211E16370038C37C /* MSDrawerController.swift in Sources */,
|
||||||
A5CEC16D20D98EE70016922A /* Colors.swift in Sources */,
|
A5CEC16D20D98EE70016922A /* Colors.swift in Sources */,
|
||||||
FD0D29D62151A3D700E8655E /* MSCardPresenterNavigationController.swift in Sources */,
|
FD0D29D62151A3D700E8655E /* MSCardPresenterNavigationController.swift in Sources */,
|
||||||
|
@ -565,6 +596,7 @@
|
||||||
FDF41ED92141A02200EC527C /* MSCalendarConfiguration.swift in Sources */,
|
FDF41ED92141A02200EC527C /* MSCalendarConfiguration.swift in Sources */,
|
||||||
FD7254E92147059D002F4069 /* Calendar+Extensions.swift in Sources */,
|
FD7254E92147059D002F4069 /* Calendar+Extensions.swift in Sources */,
|
||||||
A559BB7E212B6D100055E107 /* String+Extension.swift in Sources */,
|
A559BB7E212B6D100055E107 /* String+Extension.swift in Sources */,
|
||||||
|
A5961FA5218A260500E2A506 /* MSPopupMenuSectionHeaderView.swift in Sources */,
|
||||||
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */,
|
B46D3F9D215985AC0029772C /* MSPersonaListView.swift in Sources */,
|
||||||
A5DCA76421224026005F4CB7 /* MSSeparator.swift in Sources */,
|
A5DCA76421224026005F4CB7 /* MSSeparator.swift in Sources */,
|
||||||
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */,
|
FD599D062134A682008845EE /* AccessibleViewDelegate.swift in Sources */,
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// MARK: MSColors
|
||||||
|
|
||||||
public struct MSColors {
|
public struct MSColors {
|
||||||
// MARK: Primary
|
// MARK: Primary
|
||||||
|
|
||||||
|
@ -69,25 +71,22 @@ public struct MSColors {
|
||||||
|
|
||||||
// TODO: Add semantic colors describing colors used for particular control elements (must reference physical colors)
|
// TODO: Add semantic colors describing colors used for particular control elements (must reference physical colors)
|
||||||
|
|
||||||
|
public static let activityIndicator: UIColor = gray
|
||||||
|
public static let background: UIColor = white
|
||||||
|
public static let centeredLabelText: UIColor = primary
|
||||||
|
public static let separator: UIColor = borderLightGray
|
||||||
|
|
||||||
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 = warning
|
||||||
public static let textDestructiveHighlighted: UIColor = warning.withAlphaComponent(0.4)
|
public static let textDestructiveHighlighted: UIColor = warning.withAlphaComponent(0.4)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Avatar {
|
public struct Avatar {
|
||||||
public static let text: UIColor = white
|
public static let text: UIColor = white
|
||||||
}
|
}
|
||||||
public struct Persona {
|
|
||||||
public static let name: UIColor = black
|
|
||||||
public static let subtitle: UIColor = gray
|
|
||||||
public static let background: UIColor = white
|
|
||||||
public static let backgroundSelected: UIColor = backgroundGray
|
|
||||||
}
|
|
||||||
public static let activityIndicator: UIColor = gray
|
|
||||||
public static let background: UIColor = white
|
|
||||||
public static let centeredLabelText: UIColor = primary
|
|
||||||
public static let separator: UIColor = borderLightGray
|
|
||||||
public struct CalendarView {
|
public struct CalendarView {
|
||||||
public struct TodayCell {
|
public struct TodayCell {
|
||||||
public static let background: UIColor = white
|
public static let background: UIColor = white
|
||||||
|
@ -105,12 +104,35 @@ public struct MSColors {
|
||||||
public static let selectedCircleNormalColor: UIColor = primary
|
public static let selectedCircleNormalColor: UIColor = primary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct PageCardPresenter {
|
public struct PageCardPresenter {
|
||||||
public static let currentPageIndicatorTintColor: UIColor = white
|
public static let currentPageIndicatorTintColor: UIColor = white
|
||||||
public static let pageIndicatorTintColor: UIColor = white.withAlphaComponent(0.5)
|
public static let pageIndicatorTintColor: UIColor = white.withAlphaComponent(0.5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct Persona {
|
||||||
|
public static let name: UIColor = black
|
||||||
|
public static let subtitle: UIColor = gray
|
||||||
|
public static let background: UIColor = white
|
||||||
|
public static let backgroundSelected: UIColor = backgroundGray
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PopupMenu {
|
||||||
|
public struct Item {
|
||||||
|
public static let imageSelected: UIColor = primary
|
||||||
|
public static let title: UIColor = black
|
||||||
|
public static let titleSelected: UIColor = primary
|
||||||
|
public static let titleDisabled: UIColor = borderLightGray
|
||||||
|
public static let subtitle: UIColor = gray
|
||||||
|
public static let subtitleSelected: UIColor = primary
|
||||||
|
public static let subtitleDisabled: UIColor = borderLightGray
|
||||||
|
}
|
||||||
|
public static let sectionHeader: UIColor = darkGray
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - MSTextColorStyle
|
||||||
|
|
||||||
public enum MSTextColorStyle: Int {
|
public enum MSTextColorStyle: Int {
|
||||||
case regular
|
case regular
|
||||||
case secondary
|
case secondary
|
||||||
|
|
|
@ -35,7 +35,7 @@ open class MSDrawerController: UIViewController {
|
||||||
|
|
||||||
Transition is always animated when drawer is visible.
|
Transition is always animated when drawer is visible.
|
||||||
*/
|
*/
|
||||||
open var isExpanded: Bool = false {
|
@objc open var isExpanded: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
if isExpanded == oldValue {
|
if isExpanded == oldValue {
|
||||||
return
|
return
|
||||||
|
@ -82,6 +82,11 @@ open class MSDrawerController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `onDismiss` is called when popup menu is being dismissed.
|
||||||
|
@objc open var onDismiss: (() -> Void)?
|
||||||
|
/// `onDismissCompleted` is called after popup menu was dismissed.
|
||||||
|
@objc open var onDismissCompleted: (() -> Void)?
|
||||||
|
|
||||||
private let sourceView: UIView?
|
private let sourceView: UIView?
|
||||||
private let sourceRect: CGRect?
|
private let sourceRect: CGRect?
|
||||||
private let barButtonItem: UIBarButtonItem?
|
private let barButtonItem: UIBarButtonItem?
|
||||||
|
@ -135,7 +140,7 @@ open class MSDrawerController: UIViewController {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
private func initialize() {
|
open func initialize() {
|
||||||
modalPresentationStyle = .custom
|
modalPresentationStyle = .custom
|
||||||
transitioningDelegate = self
|
transitioningDelegate = self
|
||||||
}
|
}
|
||||||
|
@ -146,6 +151,20 @@ open class MSDrawerController: UIViewController {
|
||||||
view.isAccessibilityElement = false
|
view.isAccessibilityElement = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
if isBeingDismissed {
|
||||||
|
onDismiss?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
super.viewDidDisappear(animated)
|
||||||
|
if isBeingDismissed {
|
||||||
|
onDismissCompleted?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
open override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
super.viewWillTransition(to: size, with: coordinator)
|
||||||
if !isBeingPresented && presentationController is MSDrawerPresentationController {
|
if !isBeingPresented && presentationController is MSDrawerPresentationController {
|
||||||
|
@ -161,13 +180,13 @@ open class MSDrawerController: UIViewController {
|
||||||
|
|
||||||
private func presentationStyle(for sourceViewController: UIViewController) -> PresentationStyle {
|
private func presentationStyle(for sourceViewController: UIViewController) -> PresentationStyle {
|
||||||
guard let window = sourceViewController.view?.window else {
|
guard let window = sourceViewController.view?.window else {
|
||||||
// No window, use the device type as last resort. It will be only a problem in splitView on iPad
|
// No window, use the device type as last resort.
|
||||||
|
// It will be a problem:
|
||||||
|
// - on iPhone Plus/X in landscape orientation
|
||||||
|
// - on iPad in split view
|
||||||
return UIDevice.isPhone ? .slideover : .popover
|
return UIDevice.isPhone ? .slideover : .popover
|
||||||
}
|
}
|
||||||
if window.traitCollection.horizontalSizeClass == .compact || UIDevice.isPhonePlus {
|
return window.traitCollection.horizontalSizeClass == .compact ? .slideover : .popover
|
||||||
return .slideover
|
|
||||||
}
|
|
||||||
return .popover
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +194,7 @@ open class MSDrawerController: UIViewController {
|
||||||
|
|
||||||
extension MSDrawerController: UIViewControllerTransitioningDelegate {
|
extension MSDrawerController: UIViewControllerTransitioningDelegate {
|
||||||
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
|
||||||
if presented.presentationController is MSDrawerPresentationController {
|
if presentationStyle(for: source) == .slideover {
|
||||||
return MSDrawerTransitionAnimator(presenting: true, presentationDirection: presentationDirection)
|
return MSDrawerTransitionAnimator(presenting: true, presentationDirection: presentationDirection)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -196,6 +215,7 @@ extension MSDrawerController: UIViewControllerTransitioningDelegate {
|
||||||
let presentationController = UIPopoverPresentationController(presentedViewController: presented, presenting: presenting)
|
let presentationController = UIPopoverPresentationController(presentedViewController: presented, presenting: presenting)
|
||||||
presentationController.backgroundColor = MSColors.background
|
presentationController.backgroundColor = MSColors.background
|
||||||
presentationController.permittedArrowDirections = permittedArrowDirections
|
presentationController.permittedArrowDirections = permittedArrowDirections
|
||||||
|
presentationController.delegate = self
|
||||||
|
|
||||||
if let sourceView = sourceView {
|
if let sourceView = sourceView {
|
||||||
presentationController.sourceView = sourceView
|
presentationController.sourceView = sourceView
|
||||||
|
@ -212,3 +232,11 @@ extension MSDrawerController: UIViewControllerTransitioningDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - MSDrawerController: UIPopoverPresentationControllerDelegate
|
||||||
|
|
||||||
|
extension MSDrawerController: UIPopoverPresentationControllerDelegate {
|
||||||
|
public func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
class MSDrawerTransitionAnimator: NSObject {
|
class MSDrawerTransitionAnimator: NSObject {
|
||||||
private struct Constants {
|
private struct Constants {
|
||||||
|
static let animationDurationMin: TimeInterval = 0.15
|
||||||
static let animationDurationMax: TimeInterval = 0.25
|
static let animationDurationMax: TimeInterval = 0.25
|
||||||
static let animationSpeed: CGFloat = 1300 // pixels per second
|
static let animationSpeed: CGFloat = 1300 // pixels per second
|
||||||
}
|
}
|
||||||
|
|
||||||
static func animationDuration(forSizeChange change: CGFloat) -> TimeInterval {
|
static func animationDuration(forSizeChange change: CGFloat) -> TimeInterval {
|
||||||
return min(TimeInterval(abs(change) / Constants.animationSpeed), Constants.animationDurationMax)
|
let duration = TimeInterval(abs(change) / Constants.animationSpeed)
|
||||||
|
return max(Constants.animationDurationMin, min(duration, Constants.animationDurationMax))
|
||||||
}
|
}
|
||||||
|
|
||||||
let presenting: Bool
|
let presenting: Bool
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
public extension UIFont {
|
public extension UIFont {
|
||||||
|
var deviceLineHeight: CGFloat { return UIScreen.main.roundToDevicePixels(lineHeight) }
|
||||||
|
|
||||||
func withWeight(_ weight: UIFont.Weight) -> UIFont {
|
func withWeight(_ weight: UIFont.Weight) -> UIFont {
|
||||||
return UIFont(descriptor: fontDescriptor.withWeight(weight), size: pointSize)
|
return UIFont(descriptor: fontDescriptor.withWeight(weight), size: pointSize)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
public extension UIImage {
|
||||||
|
/// Full replacement for `UIImage(named:)` which attempts to recolor image assets with a color from the high contrast
|
||||||
|
/// color palette when `Darken Colors` is enabled. The method is called `static` because the images are outputted with
|
||||||
|
/// the rendering mode `AlwaysOriginal` with the intention of preventing further recoloring by `tintColor`.
|
||||||
|
@objc class func staticImageNamed(_ name: String, in bundle: Bundle? = nil, withPrimaryColorForDarkerSystemColors primaryColor: UIColor? = nil) -> UIImage? {
|
||||||
|
guard var image = UIImage(named: name, in: bundle, compatibleWith: nil) else {
|
||||||
|
NSLog("Missing image asset with name: \(name)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if UIAccessibilityDarkerSystemColorsEnabled(), let primaryColor = primaryColor {
|
||||||
|
// Recolor image with high contrast version of `primaryColor`
|
||||||
|
image = recolorImage(image, withPrimaryColor: primaryColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force image to be `AlwaysOriginal` regardless of the setting in `.xcassets` to prevent recoloring caused by `tintColor`
|
||||||
|
return image.withRenderingMode(.alwaysOriginal)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class func staticImageNamed(_ name: String) -> UIImage? {
|
||||||
|
// TODO: Provide primary color for known images
|
||||||
|
return staticImageNamed(name, in: OfficeUIFabricFramework.bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class func recolorImage(_ originalImage: UIImage, withPrimaryColor primaryColor: UIColor) -> UIImage {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(originalImage.size, false, originalImage.scale)
|
||||||
|
|
||||||
|
// Fill canvas with `primaryColor`
|
||||||
|
primaryColor.setFill()
|
||||||
|
UIRectFill(CGRect(x: 0.0, y: 0.0, width: originalImage.size.width, height: originalImage.size.height))
|
||||||
|
|
||||||
|
// Apply the `alpha` component of the image to the canvas
|
||||||
|
originalImage.draw(at: CGPoint.zero, blendMode: .destinationIn, alpha: 1.0)
|
||||||
|
|
||||||
|
// Create `image`
|
||||||
|
let image = UIGraphicsGetImageFromCurrentImageContext()!
|
||||||
|
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc public func image(withPrimaryColor primaryColor: UIColor) -> UIImage {
|
||||||
|
// Force image to be `AlwaysOriginal` regardless of the setting in `.xcassets` to prevent recoloring caused by `tintColor`
|
||||||
|
return UIImage.recolorImage(self, withPrimaryColor: primaryColor).withRenderingMode(.alwaysOriginal)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
`MSPopupMenuController` is used to present a popup menu that slides from top or bottom depending on `presentationDirection`. Use `presentationOrigin` to specify the vertical offset (in screen coordinates) from which to show popup menu. If not provided it will be calculated automatically: bottom of navigation bar for `.down` presentation and bottom of the screen for `.up` presentation.
|
||||||
|
|
||||||
|
`MSPopupMenuController` will be presented as a popover on iPad and so requires either `sourceView`/`sourceRect` or `barButtonItem` to be provided via available initializers. Use `permittedArrowDirections` to specify the direction of the popover arrow.
|
||||||
|
*/
|
||||||
|
open class MSPopupMenuController: MSDrawerController {
|
||||||
|
private struct Constants {
|
||||||
|
static let minimumWidthForPopover: CGFloat = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
open override var preferredContentSize: CGSize {
|
||||||
|
get { return CGSize(width: preferredWidth, height: preferredHeight) }
|
||||||
|
set { }
|
||||||
|
}
|
||||||
|
open override var preferredWidth: CGFloat {
|
||||||
|
guard presentationController is UIPopoverPresentationController else {
|
||||||
|
return super.preferredWidth
|
||||||
|
}
|
||||||
|
var width = Constants.minimumWidthForPopover
|
||||||
|
for section in sections {
|
||||||
|
width = max(width, MSPopupMenuSectionHeaderView.preferredWidth(for: section))
|
||||||
|
for item in section.items {
|
||||||
|
width = max(width, MSPopupMenuItemCell.preferredWidth(for: item, preservingSpaceForImage: itemsHaveImages))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
open var preferredHeight: CGFloat {
|
||||||
|
var height: CGFloat = 0
|
||||||
|
for section in sections {
|
||||||
|
height += MSPopupMenuSectionHeaderView.preferredHeight(for: section)
|
||||||
|
for item in section.items {
|
||||||
|
height += MSPopupMenuItemCell.preferredHeight(for: item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use `selectedItemIndexPath` to get or set the selected menu item instead of doing this via `MSPopupMenuItem` directly
|
||||||
|
@objc open var selectedItemIndexPath: IndexPath? {
|
||||||
|
get {
|
||||||
|
for (sectionIndex, section) in sections.enumerated() {
|
||||||
|
for (itemIndex, item) in section.items.enumerated() {
|
||||||
|
if item.isSelected {
|
||||||
|
return IndexPath(item: itemIndex, section: sectionIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
for (sectionIndex, section) in sections.enumerated() {
|
||||||
|
for (itemIndex, item) in section.items.enumerated() {
|
||||||
|
item.isSelected = sectionIndex == newValue?.section && itemIndex == newValue?.item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var sections: [MSPopupMenuSection] = []
|
||||||
|
|
||||||
|
private var tableView = UITableView()
|
||||||
|
|
||||||
|
private var itemsHaveImages: Bool {
|
||||||
|
return sections.contains(where: { $0.items.contains(where: { $0.image != nil }) })
|
||||||
|
}
|
||||||
|
private var needsScrolling: Bool {
|
||||||
|
return tableView.contentSize.height > tableView.bounds.height
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append new items to the last section of the menu
|
||||||
|
/// - note: If there is no section in the menu, create a new one without header and append the items to it
|
||||||
|
@objc public func addItems(_ items: [MSPopupMenuItem]) {
|
||||||
|
if let section = sections.last {
|
||||||
|
section.items.append(contentsOf: items)
|
||||||
|
} else {
|
||||||
|
let section = MSPopupMenuSection(title: nil, items: items)
|
||||||
|
sections.append(section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append a new section to the end of menu
|
||||||
|
@objc public func addSection(_ section: MSPopupMenuSection) {
|
||||||
|
sections.append(section)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append new sections to the end of menu
|
||||||
|
@objc public func addSections(_ sections: [MSPopupMenuSection]) {
|
||||||
|
self.sections.append(contentsOf: sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func initialize() {
|
||||||
|
super.initialize()
|
||||||
|
|
||||||
|
tableView.backgroundColor = .clear
|
||||||
|
tableView.separatorStyle = .none
|
||||||
|
// Prevent automatic insetting of this non-scrollable content
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
tableView.contentInsetAdjustmentBehavior = .never
|
||||||
|
}
|
||||||
|
tableView.isAccessibilityElement = true
|
||||||
|
|
||||||
|
// Prevent tap delay when selecting a menu item
|
||||||
|
tableView.delaysContentTouches = false
|
||||||
|
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture))
|
||||||
|
panGestureRecognizer.delegate = self
|
||||||
|
tableView.addGestureRecognizer(panGestureRecognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
view.addSubview(tableView)
|
||||||
|
tableView.fitIntoSuperview()
|
||||||
|
|
||||||
|
tableView.register(MSPopupMenuItemCell.self, forCellReuseIdentifier: MSPopupMenuItemCell.identifier)
|
||||||
|
tableView.register(MSPopupMenuSectionHeaderView.self, forHeaderFooterViewReuseIdentifier: MSPopupMenuSectionHeaderView.identifier)
|
||||||
|
tableView.delegate = self
|
||||||
|
tableView.dataSource = self
|
||||||
|
}
|
||||||
|
|
||||||
|
open override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
tableView.selectRow(at: selectedItemIndexPath, animated: false, scrollPosition: .none)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func handlePanGesture(gestureRecognizer: UIGestureRecognizer) {
|
||||||
|
let point = gestureRecognizer.location(in: tableView)
|
||||||
|
|
||||||
|
switch gestureRecognizer.state {
|
||||||
|
case .began, .changed:
|
||||||
|
// Warm up all the visible cell feedback generators
|
||||||
|
// Each cell has its own generator to allow each to fire perfectly on time when highlight changes
|
||||||
|
for case let cell as MSPopupMenuItemCell in tableView.visibleCells {
|
||||||
|
if cell.feedbackGenerator == nil {
|
||||||
|
cell.feedbackGenerator = UISelectionFeedbackGenerator()
|
||||||
|
}
|
||||||
|
cell.feedbackGenerator?.prepare()
|
||||||
|
}
|
||||||
|
|
||||||
|
var cell: UITableViewCell?
|
||||||
|
if let indexPath = tableView.indexPathForRow(at: point) {
|
||||||
|
cell = tableView.cellForRow(at: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
for visibleCell in tableView.visibleCells {
|
||||||
|
visibleCell.isHighlighted = visibleCell == cell
|
||||||
|
}
|
||||||
|
|
||||||
|
case .ended, .cancelled, .failed:
|
||||||
|
guard let indexPath = tableView.indexPathForRow(at: point) else {
|
||||||
|
// Gesture did not finish on a highlighted cell, dismiss the dropdown
|
||||||
|
presentingViewController?.dismiss(animated: true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = sections[indexPath.section].items[indexPath.row]
|
||||||
|
if !item.isEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||||
|
tableView(tableView, didSelectRowAt: indexPath)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSPopupMenuController: UITableViewDataSource
|
||||||
|
|
||||||
|
extension MSPopupMenuController: UITableViewDataSource {
|
||||||
|
public func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
return sections.count
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
|
return sections[section].items.count
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
|
let section = indexPath.section
|
||||||
|
let row = indexPath.row
|
||||||
|
let item = sections[section].items[row]
|
||||||
|
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: MSPopupMenuItemCell.identifier) as! MSPopupMenuItemCell
|
||||||
|
cell.setup(item: item)
|
||||||
|
cell.preservesSpaceForImage = itemsHaveImages
|
||||||
|
cell.showsSeparator = section != tableView.numberOfSections - 1 || row != tableView.numberOfRows(inSection: section) - 1
|
||||||
|
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSPopupMenuController: UITableViewDelegate
|
||||||
|
|
||||||
|
extension MSPopupMenuController: UITableViewDelegate {
|
||||||
|
public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||||
|
return MSPopupMenuSectionHeaderView.preferredHeight(for: sections[section])
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||||
|
let item = sections[indexPath.section].items[indexPath.row]
|
||||||
|
return MSPopupMenuItemCell.preferredHeight(for: item)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
||||||
|
let section = sections[section]
|
||||||
|
if !MSPopupMenuSectionHeaderView.isHeaderVisible(for: section) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: MSPopupMenuSectionHeaderView.identifier) as! MSPopupMenuSectionHeaderView
|
||||||
|
headerView.setup(section: section)
|
||||||
|
return headerView
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||||
|
let item = sections[indexPath.section].items[indexPath.row]
|
||||||
|
return item.isEnabled ? indexPath : nil
|
||||||
|
}
|
||||||
|
|
||||||
|
public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
selectedItemIndexPath = indexPath
|
||||||
|
sections[indexPath.section].items[indexPath.row].onSelected?()
|
||||||
|
if !isBeingDismissed {
|
||||||
|
presentingViewController?.dismiss(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - MSPopupMenuController: UIGestureRecognizerDelegate
|
||||||
|
|
||||||
|
extension MSPopupMenuController: UIGestureRecognizerDelegate {
|
||||||
|
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
|
return !needsScrolling
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
`MSPopupMenuItem` represents a menu item inside `MSPopupMenuController`.
|
||||||
|
*/
|
||||||
|
@objcMembers
|
||||||
|
open class MSPopupMenuItem: NSObject {
|
||||||
|
public let image: UIImage?
|
||||||
|
public let selectedImage: UIImage?
|
||||||
|
public let title: String
|
||||||
|
public let subtitle: String?
|
||||||
|
|
||||||
|
public var isEnabled: Bool = true
|
||||||
|
public var isSelected: Bool = false
|
||||||
|
|
||||||
|
public let onSelected: (() -> Void)?
|
||||||
|
|
||||||
|
public init(image: UIImage? = nil, selectedImage: UIImage? = nil, title: String, subtitle: String? = nil, isEnabled: Bool = true, isSelected: Bool = false, onSelected: (() -> Void)? = nil) {
|
||||||
|
self.image = image
|
||||||
|
self.selectedImage = selectedImage ?? image
|
||||||
|
self.title = title
|
||||||
|
self.subtitle = subtitle
|
||||||
|
self.isEnabled = isEnabled
|
||||||
|
self.isSelected = isSelected
|
||||||
|
self.onSelected = onSelected
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(imageName: String, generateSelectedImage: Bool = true, title: String, subtitle: String? = nil, isEnabled: Bool = true, isSelected: Bool = false, onSelected: (() -> Void)? = nil) {
|
||||||
|
let image = UIImage.staticImageNamed(imageName, in: nil)
|
||||||
|
let selectedImage = generateSelectedImage ? image?.image(withPrimaryColor: MSColors.PopupMenu.Item.imageSelected) : nil
|
||||||
|
self.init(image: image, selectedImage: selectedImage, title: title, subtitle: subtitle, isEnabled: isEnabled, isSelected: isSelected, onSelected: onSelected)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
class MSPopupMenuItemCell: UITableViewCell {
|
||||||
|
private struct Constants {
|
||||||
|
static let oneLineHeight: CGFloat = 50.0
|
||||||
|
static let twoLineHeight: CGFloat = 62.0
|
||||||
|
|
||||||
|
static let horizontalSpacing: CGFloat = 16.0
|
||||||
|
static let verticalSpacing: CGFloat = 2.0
|
||||||
|
|
||||||
|
static let imageViewSize: CGFloat = 25.0
|
||||||
|
static let selectedImageViewSize: CGFloat = 20.0
|
||||||
|
|
||||||
|
static let titleFontStyle: MSTextStyle = .body
|
||||||
|
static let subtitleFontStyle: MSTextStyle = .footnote
|
||||||
|
|
||||||
|
static let defaultAlpha: CGFloat = 1.0
|
||||||
|
static let highlightedAlpha: CGFloat = 0.4
|
||||||
|
|
||||||
|
static let animationDuration: TimeInterval = 0.15
|
||||||
|
}
|
||||||
|
|
||||||
|
static let identifier: String = "MSPopupMenuItemCell"
|
||||||
|
|
||||||
|
static func preferredWidth(for item: MSPopupMenuItem, preservingSpaceForImage preserveSpaceForImage: Bool) -> CGFloat {
|
||||||
|
let labelFont = Constants.titleFontStyle.font
|
||||||
|
let labelSize = item.title.boundingRect(
|
||||||
|
with: CGSize(width: .greatestFiniteMagnitude, height: labelFont.deviceLineHeight),
|
||||||
|
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||||
|
attributes: [.font: labelFont],
|
||||||
|
context: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
let spacing = Constants.horizontalSpacing
|
||||||
|
var width = spacing + labelSize.width + spacing + Constants.selectedImageViewSize + spacing
|
||||||
|
|
||||||
|
if item.image != nil || preserveSpaceForImage {
|
||||||
|
width += Constants.imageViewSize + spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
return width
|
||||||
|
}
|
||||||
|
|
||||||
|
static func preferredHeight(for item: MSPopupMenuItem) -> CGFloat {
|
||||||
|
return item.subtitle == nil ? Constants.oneLineHeight : Constants.twoLineHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
var feedbackGenerator: UISelectionFeedbackGenerator?
|
||||||
|
var preservesSpaceForImage: Bool = false
|
||||||
|
var showsSeparator: Bool = true {
|
||||||
|
didSet {
|
||||||
|
separator.isHidden = !showsSeparator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var item: MSPopupMenuItem?
|
||||||
|
|
||||||
|
// Cannot use imageView since it exists in superclass
|
||||||
|
private let _imageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.contentMode = .center
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let titleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = Constants.titleFontStyle.font
|
||||||
|
label.lineBreakMode = .byTruncatingTail
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let subtitleLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = Constants.subtitleFontStyle.font
|
||||||
|
label.lineBreakMode = .byTruncatingTail
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let selectedImageView: UIImageView = {
|
||||||
|
let imageView = UIImageView()
|
||||||
|
imageView.image = UIImage.staticImageNamed("checkmark-blue-20x20")
|
||||||
|
imageView.contentMode = .center
|
||||||
|
return imageView
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let separator = MSSeparator()
|
||||||
|
|
||||||
|
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
|
||||||
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
backgroundColor = .clear
|
||||||
|
selectionStyle = .none
|
||||||
|
|
||||||
|
addSubview(_imageView)
|
||||||
|
addSubview(titleLabel)
|
||||||
|
addSubview(subtitleLabel)
|
||||||
|
addSubview(selectedImageView)
|
||||||
|
addSubview(separator)
|
||||||
|
|
||||||
|
isAccessibilityElement = true
|
||||||
|
accessibilityTraits = UIAccessibilityTraitButton
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(item: MSPopupMenuItem) {
|
||||||
|
self.item = item
|
||||||
|
|
||||||
|
_imageView.image = item.image
|
||||||
|
_imageView.highlightedImage = item.selectedImage
|
||||||
|
_imageView.isHidden = _imageView.image == nil
|
||||||
|
|
||||||
|
titleLabel.text = item.title
|
||||||
|
subtitleLabel.text = item.subtitle
|
||||||
|
|
||||||
|
updateViews()
|
||||||
|
|
||||||
|
var accessibilityString = "\(item.title)"
|
||||||
|
if let subtitle = item.subtitle {
|
||||||
|
accessibilityString.append(", \(subtitle)")
|
||||||
|
}
|
||||||
|
accessibilityLabel = accessibilityString
|
||||||
|
if item.isEnabled {
|
||||||
|
accessibilityTraits &= ~UIAccessibilityTraitNotEnabled
|
||||||
|
} else {
|
||||||
|
accessibilityTraits |= UIAccessibilityTraitNotEnabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
var leftOffset: CGFloat = safeAreaInsetsIfAvailable.left + Constants.horizontalSpacing
|
||||||
|
let rightOffset = bounds.width - safeAreaInsetsIfAvailable.right
|
||||||
|
|
||||||
|
if !_imageView.isHidden || preservesSpaceForImage {
|
||||||
|
_imageView.frame = CGRect(
|
||||||
|
x: leftOffset,
|
||||||
|
y: UIScreen.main.middleOrigin(bounds.height, containedSizeValue: Constants.imageViewSize),
|
||||||
|
width: Constants.imageViewSize,
|
||||||
|
height: Constants.imageViewSize
|
||||||
|
)
|
||||||
|
|
||||||
|
leftOffset += _imageView.width + Constants.horizontalSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedImageView.frame = CGRect(
|
||||||
|
x: rightOffset - Constants.horizontalSpacing - Constants.selectedImageViewSize,
|
||||||
|
y: UIScreen.main.middleOrigin(height, containedSizeValue: Constants.selectedImageViewSize),
|
||||||
|
width: Constants.selectedImageViewSize,
|
||||||
|
height: Constants.selectedImageViewSize
|
||||||
|
)
|
||||||
|
|
||||||
|
separator.frame = CGRect(x: leftOffset, y: bounds.height - separator.height, width: rightOffset - leftOffset, height: separator.height)
|
||||||
|
|
||||||
|
var labelWidth = rightOffset - Constants.horizontalSpacing - leftOffset
|
||||||
|
if !selectedImageView.isHidden {
|
||||||
|
labelWidth -= Constants.horizontalSpacing + selectedImageView.width
|
||||||
|
}
|
||||||
|
let titleLabelHeight = titleLabel.font.deviceLineHeight
|
||||||
|
let subtitleLabelHeight = subtitleLabel.font.deviceLineHeight
|
||||||
|
let isSubtitleVisible = subtitleLabel.text != nil
|
||||||
|
var labelAreaHeight = titleLabelHeight
|
||||||
|
if isSubtitleVisible {
|
||||||
|
labelAreaHeight += Constants.verticalSpacing + subtitleLabelHeight
|
||||||
|
}
|
||||||
|
var labelTop = UIScreen.main.middleOrigin(bounds.height, containedSizeValue: labelAreaHeight)
|
||||||
|
|
||||||
|
titleLabel.frame = CGRect(x: leftOffset, y: labelTop, width: labelWidth, height: titleLabelHeight)
|
||||||
|
if isSubtitleVisible {
|
||||||
|
labelTop += titleLabel.height + Constants.verticalSpacing
|
||||||
|
subtitleLabel.frame = CGRect(x: leftOffset, y: labelTop, width: labelWidth, height: subtitleLabelHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||||
|
let oldValue = isHighlighted
|
||||||
|
super.setHighlighted(highlighted, animated: animated)
|
||||||
|
if isHighlighted == oldValue {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default background color change
|
||||||
|
backgroundColor = .clear
|
||||||
|
|
||||||
|
// Give feedback if needed
|
||||||
|
if highlighted {
|
||||||
|
feedbackGenerator?.selectionChanged()
|
||||||
|
feedbackGenerator?.prepare()
|
||||||
|
}
|
||||||
|
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: Constants.animationDuration) {
|
||||||
|
self.updateViews()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateViews()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||||
|
let oldValue = isSelected
|
||||||
|
super.setSelected(selected, animated: animated)
|
||||||
|
if isSelected == oldValue {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default background color change
|
||||||
|
backgroundColor = .clear
|
||||||
|
|
||||||
|
if animated {
|
||||||
|
UIView.animate(withDuration: Constants.animationDuration) {
|
||||||
|
self.updateViews()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateViews()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateViews() {
|
||||||
|
if item?.isEnabled == false {
|
||||||
|
titleLabel.textColor = MSColors.PopupMenu.Item.titleDisabled
|
||||||
|
subtitleLabel.textColor = MSColors.PopupMenu.Item.subtitleDisabled
|
||||||
|
} else {
|
||||||
|
// Highlight
|
||||||
|
let alpha = isHighlighted ? Constants.highlightedAlpha : Constants.defaultAlpha
|
||||||
|
_imageView.alpha = alpha
|
||||||
|
titleLabel.alpha = alpha
|
||||||
|
subtitleLabel.alpha = alpha
|
||||||
|
|
||||||
|
// Selection
|
||||||
|
_imageView.isHighlighted = isSelected
|
||||||
|
titleLabel.textColor = isSelected ? MSColors.PopupMenu.Item.titleSelected : MSColors.PopupMenu.Item.title
|
||||||
|
subtitleLabel.textColor = isSelected ? MSColors.PopupMenu.Item.subtitleSelected : MSColors.PopupMenu.Item.subtitle
|
||||||
|
}
|
||||||
|
selectedImageView.isHidden = !isSelected
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
`MSPopupMenuSection` represents a section of menu items inside `MSPopupMenuController`.
|
||||||
|
*/
|
||||||
|
@objcMembers
|
||||||
|
open class MSPopupMenuSection: NSObject {
|
||||||
|
public let title: String?
|
||||||
|
public var items: [MSPopupMenuItem]
|
||||||
|
|
||||||
|
public init(title: String?, items: [MSPopupMenuItem]) {
|
||||||
|
self.title = title
|
||||||
|
self.items = items
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
//
|
||||||
|
// Copyright © 2018 Microsoft Corporation. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
class MSPopupMenuSectionHeaderView: UITableViewHeaderFooterView {
|
||||||
|
private struct Constants {
|
||||||
|
static let padding = UIEdgeInsets(top: 14, left: 16, bottom: 0, right: 16)
|
||||||
|
static let titleFontStyle: MSTextStyle = .subhead
|
||||||
|
}
|
||||||
|
|
||||||
|
static let identifier: String = "MSPopupMenuSectionHeaderView"
|
||||||
|
|
||||||
|
static func isHeaderVisible(for section: MSPopupMenuSection) -> Bool {
|
||||||
|
return section.title != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
static func preferredWidth(for section: MSPopupMenuSection) -> CGFloat {
|
||||||
|
guard isHeaderVisible(for: section), let title = section.title else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
let labelFont = Constants.titleFontStyle.font
|
||||||
|
let labelSize = title.boundingRect(
|
||||||
|
with: CGSize(width: .greatestFiniteMagnitude, height: labelFont.deviceLineHeight),
|
||||||
|
options: [.usesLineFragmentOrigin, .usesFontLeading],
|
||||||
|
attributes: [.font: labelFont],
|
||||||
|
context: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
return Constants.padding.left + labelSize.width + Constants.padding.right
|
||||||
|
}
|
||||||
|
|
||||||
|
static func preferredHeight(for section: MSPopupMenuSection) -> CGFloat {
|
||||||
|
if isHeaderVisible(for: section) {
|
||||||
|
return Constants.padding.top + Constants.titleFontStyle.font.deviceLineHeight + Constants.padding.bottom
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let label: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.font = Constants.titleFontStyle.font
|
||||||
|
label.textColor = MSColors.PopupMenu.sectionHeader
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
|
override init(reuseIdentifier: String?) {
|
||||||
|
super.init(reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
|
let view = UIView(frame: .zero)
|
||||||
|
view.backgroundColor = .clear
|
||||||
|
backgroundView = view
|
||||||
|
|
||||||
|
contentView.addSubview(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(section: MSPopupMenuSection) {
|
||||||
|
label.text = section.title
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews() {
|
||||||
|
super.layoutSubviews()
|
||||||
|
let labelSize = label.sizeThatFits(bounds.size)
|
||||||
|
label.frame = CGRect(origin: CGPoint(x: Constants.padding.left, y: Constants.padding.top), size: labelSize)
|
||||||
|
}
|
||||||
|
}
|
12
OfficeUIFabric/Resources/Assets.xcassets/checkmark-blue-20x20.imageset/Contents.json
поставляемый
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "checkmark-20x20.pdf"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
}
|
||||||
|
}
|