Commit 1fd7ceeb by Daniel Dahan

development: updated FABMenuItem to hide titleLabel when title value is empty

parent 5a438b88
......@@ -101,7 +101,7 @@
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7941CB40DC500C806FE /* RobotoFont.swift */; };
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9628645E1D540AF300690B69 /* DynamicFontType.swift */; };
965E810D1DD4D5C800D61E4B /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB78E1CB40DC500C806FE /* Menu.swift */; };
965E810E1DD4D5C800D61E4B /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9697F7B61D8F22A4004741EC /* MenuItem.swift */; };
965E810E1DD4D5C800D61E4B /* FABMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9697F7B61D8F22A4004741EC /* FABMenuItem.swift */; };
965E810F1DD4D5C800D61E4B /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB78F1CB40DC500C806FE /* MenuController.swift */; };
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7901CB40DC500C806FE /* NavigationBar.swift */; };
965E81111DD4D5C800D61E4B /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96BCB7911CB40DC500C806FE /* NavigationController.swift */; };
......@@ -135,7 +135,7 @@
9697F7C11D8F2572004741EC /* Material+Array.swift in Headers */ = {isa = PBXBuildFile; fileRef = 96C1C8801D42C62800E6608F /* Material+Array.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C21D8F2572004741EC /* Material+UIWindow.swift in Headers */ = {isa = PBXBuildFile; fileRef = 962864591D53FE3E00690B69 /* Material+UIWindow.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C31D8F2572004741EC /* DynamicFontType.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9628645E1D540AF300690B69 /* DynamicFontType.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C51D8F2573004741EC /* MenuItem.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9697F7B61D8F22A4004741EC /* MenuItem.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7C51D8F2573004741EC /* FABMenuItem.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9697F7B61D8F22A4004741EC /* FABMenuItem.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7CB1D8F2573004741EC /* Snackbar.swift in Headers */ = {isa = PBXBuildFile; fileRef = 963FBEFC1D669510008F8512 /* Snackbar.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7CC1D8F2573004741EC /* SnackbarController.swift in Headers */ = {isa = PBXBuildFile; fileRef = 961EFC571D738FF600E84652 /* SnackbarController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
9697F7CD1D8F2582004741EC /* Color.swift in Headers */ = {isa = PBXBuildFile; fileRef = 9661222D1D3EC414008BB4CB /* Color.swift */; settings = {ATTRIBUTES = (Public, ); }; };
......@@ -266,7 +266,7 @@
967887881C9777CB0037F6C9 /* MaterialViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaterialViewTests.swift; sourceTree = "<group>"; };
967A48181D0F425A00B8CEB7 /* StatusBarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarController.swift; sourceTree = "<group>"; };
968C99461D377849000074FF /* Offset.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Offset.swift; sourceTree = "<group>"; };
9697F7B61D8F22A4004741EC /* MenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = "<group>"; };
9697F7B61D8F22A4004741EC /* FABMenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABMenuItem.swift; sourceTree = "<group>"; };
96A183621E0C6CE200083C30 /* FABMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABMenu.swift; sourceTree = "<group>"; };
96A183641E0C6DD400083C30 /* FABMenuController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABMenuController.swift; sourceTree = "<group>"; };
96A183661E0C6DE100083C30 /* FABToToolbarController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FABToToolbarController.swift; sourceTree = "<group>"; };
......@@ -549,9 +549,9 @@
isa = PBXGroup;
children = (
96BCB78E1CB40DC500C806FE /* Menu.swift */,
9697F7B61D8F22A4004741EC /* MenuItem.swift */,
96BCB78F1CB40DC500C806FE /* MenuController.swift */,
96A183621E0C6CE200083C30 /* FABMenu.swift */,
9697F7B61D8F22A4004741EC /* FABMenuItem.swift */,
96A183641E0C6DD400083C30 /* FABMenuController.swift */,
96A183661E0C6DE100083C30 /* FABToToolbarController.swift */,
);
......@@ -932,7 +932,7 @@
9697F7C11D8F2572004741EC /* Material+Array.swift in Headers */,
9697F7C21D8F2572004741EC /* Material+UIWindow.swift in Headers */,
9697F7C31D8F2572004741EC /* DynamicFontType.swift in Headers */,
9697F7C51D8F2573004741EC /* MenuItem.swift in Headers */,
9697F7C51D8F2573004741EC /* FABMenuItem.swift in Headers */,
9697F7CB1D8F2573004741EC /* Snackbar.swift in Headers */,
9697F7CC1D8F2573004741EC /* SnackbarController.swift in Headers */,
9617B07D1DFCA8CF00410F8F /* Application.swift in Headers */,
......@@ -1171,7 +1171,7 @@
965E810B1DD4D5C800D61E4B /* RobotoFont.swift in Sources */,
965E810C1DD4D5C800D61E4B /* DynamicFontType.swift in Sources */,
965E810D1DD4D5C800D61E4B /* Menu.swift in Sources */,
965E810E1DD4D5C800D61E4B /* MenuItem.swift in Sources */,
965E810E1DD4D5C800D61E4B /* FABMenuItem.swift in Sources */,
965E810F1DD4D5C800D61E4B /* MenuController.swift in Sources */,
96A183651E0C6DD400083C30 /* FABMenuController.swift in Sources */,
965E81101DD4D5C800D61E4B /* NavigationBar.swift in Sources */,
......
......@@ -41,28 +41,28 @@ public enum FABMenuDirection: Int {
@objc(FABMenuDelegate)
public protocol FABMenuDelegate {
/**
A delegation method that is execited when the menu will open.
A delegation method that is execited when the fabMenu will open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillOpen(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu did open.
A delegation method that is execited when the fabMenu did open.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuDidOpen(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu will close.
A delegation method that is execited when the fabMenu will close.
- Parameter fabMenu: A FABMenu.
*/
@objc
optional func fabMenuWillClose(fabMenu: FABMenu)
/**
A delegation method that is execited when the menu did close.
A delegation method that is execited when the fabMenu did close.
- Parameter fabMenu: A FABMenu.
*/
@objc
......@@ -203,7 +203,21 @@ extension FABMenu {
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
delegate?.fabMenuWillOpen?(fabMenu: self)
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations) { [weak self, completion = completion] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.showTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidOpen?(fabMenu: s)
}
completion?(view)
}
}
/**
......@@ -217,7 +231,21 @@ extension FABMenu {
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
delegate?.fabMenuWillClose?(fabMenu: self)
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations) { [weak self, completion = completion] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.hideTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidClose?(fabMenu: s)
}
completion?(view)
}
}
}
......@@ -243,7 +271,7 @@ extension FABMenu {
delegate?.fabMenu?(fabMenu: self, tappedAt: point, isOutside: true)
closeMenu()
close()
return self.hitTest(point, with: event)
}
......@@ -257,46 +285,10 @@ extension FABMenu {
@objc
fileprivate func handleToggleMenu(button: UIButton) {
guard isOpened else {
openMenu()
open()
return
}
closeMenu()
}
}
extension FABMenu {
/// Opens the menu and reveals the FABMenuItems.
open func openMenu() {
delegate?.fabMenuWillOpen?(fabMenu: self)
open { [weak self] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.showTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidOpen?(fabMenu: s)
}
}
}
/// Closes the menu and hides the FABMenuItems.
open func closeMenu() {
delegate?.fabMenuWillClose?(fabMenu: self)
close { [weak self] (view) in
guard let s = self else {
return
}
(view as? FABMenuItem)?.hideTitleLabel()
if view == s.items.last {
s.delegate?.fabMenuDidClose?(fabMenu: s)
}
}
close()
}
}
......@@ -59,10 +59,24 @@ open class FABMenuItem: View {
}
set(value) {
titleLabel.text = value
hideTitleLabel()
layoutSubviews()
}
}
open override func layoutSubviews() {
super.layoutSubviews()
guard let t = title, 0 < t.utf16.count else {
titleLabel.removeFromSuperview()
return
}
if nil == titleLabel.superview {
addSubview(titleLabel)
}
}
}
extension FABMenuItem {
/// Shows the titleLabel.
open func showTitleLabel() {
let interimSpace = InterimSpacePresetToValue(preset: .interimSpace6)
......@@ -88,19 +102,20 @@ open class FABMenuItem: View {
open func hideTitleLabel() {
titleLabel.isHidden = true
}
}
extension FABMenuItem {
/// Prepares the button.
private func prepareButton() {
fileprivate func prepareButton() {
layout(button).edges()
}
/// Prepares the titleLabel.
private func prepareTitleLabel() {
fileprivate func prepareTitleLabel() {
titleLabel.font = RobotoFont.regular(with: 14)
titleLabel.textAlignment = .center
titleLabel.backgroundColor = .white
titleLabel.depthPreset = button.depthPreset
titleLabel.cornerRadiusPreset = .cornerRadius1
addSubview(titleLabel)
}
}
......@@ -41,7 +41,36 @@ public enum MenuDirection: Int {
@objc(MenuDelegate)
public protocol MenuDelegate {
/**
Gets called when the user taps while the menu is opened.
A delegation method that is execited when the menu will open.
- Parameter menu: A Menu.
*/
@objc
optional func menuWillOpen(menu: Menu)
/**
A delegation method that is execited when the menu did open.
- Parameter menu: A Menu.
*/
@objc
optional func menuDidOpen(menu: Menu)
/**
A delegation method that is execited when the menu will close.
- Parameter menu: A Menu.
*/
@objc
optional func menuWillClose(menu: Menu)
/**
A delegation method that is execited when the menu did close.
- Parameter menu: A Menu.
*/
@objc
optional func menuDidClose(menu: Menu)
/**
A delegation method that is executed when the user taps while
the menu is opened.
- Parameter menu: A Menu.
- Parameter tappedAt point: A CGPoint.
- Parameter isOutside: A boolean indicating whether the tap
......@@ -57,9 +86,6 @@ open class Menu: View, SpringableMotion {
/// A reference to the SpringMotion object.
internal let spring = SpringMotion()
/// An optional delegation handler.
open weak var delegate: MenuDelegate?
/// The direction in which the animation opens the menu.
open var springDirection = SpringDirection.up {
didSet {
......@@ -67,15 +93,132 @@ open class Menu: View, SpringableMotion {
}
}
/// A reference to the base UIButton.
open var button: UIButton? {
didSet {
oldValue?.removeFromSuperview()
guard let v = button else {
return
}
addSubview(v)
v.addTarget(self, action: #selector(handleToggleMenu(button:)), for: .touchUpInside)
}
}
/// Size of MenuItems.
open var itemSize: CGSize {
get {
return spring.itemSize
}
set(value) {
spring.itemSize = value
}
}
/// A preset wrapper around interimSpace.
open var interimSpacePreset: InterimSpacePreset {
get {
return spring.interimSpacePreset
}
set(value) {
spring.interimSpacePreset = value
}
}
/// The space between views.
open var interimSpace: InterimSpace {
get {
return spring.interimSpace
}
set(value) {
spring.interimSpace = value
}
}
/// A boolean indicating if the menu is open or not.
open var isOpened: Bool {
get {
return spring.isOpened
}
set(value) {
spring.isOpened = value
}
}
/// A boolean indicating if the menu is enabled.
open var isEnabled: Bool {
get {
return spring.isEnabled
}
set(value) {
spring.isEnabled = value
}
}
/// An optional delegation handler.
open weak var delegate: MenuDelegate?
/// A reference to the MenuItems
open var views: [UIView] {
open var items: [UIView] {
get {
return spring.views
return spring.views as! [UIView]
}
set(value) {
for v in spring.views {
v.removeFromSuperview()
}
for v in value {
addSubview(v)
}
spring.views = value
}
}
open override func layoutSubviews() {
super.layoutSubviews()
button?.frame.size = bounds.size
spring.baseSize = bounds.size
}
open override func prepare() {
super.prepare()
backgroundColor = nil
interimSpacePreset = .interimSpace6
}
}
extension Menu {
/**
Open the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
/**
Close the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
fileprivate func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
}
extension Menu {
......@@ -86,7 +229,7 @@ extension Menu {
- Returns: An optional UIView.
*/
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
guard spring.isOpened, spring.isEnabled else {
guard isOpened, isEnabled else {
return super.hitTest(point, with: event)
}
......@@ -100,36 +243,24 @@ extension Menu {
delegate?.menu?(menu: self, tappedAt: point, isOutside: true)
close()
return self.hitTest(point, with: event)
}
}
extension Menu {
/**
Open the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
*/
open func open(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.expand(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
}
/**
Close the Menu component with animation options.
- Parameter duration: The time for each view's animation.
- Parameter delay: A delay time for each view's animation.
- Parameter usingSpringWithDamping: A damping ratio for the animation.
- Parameter initialSpringVelocity: The initial velocity for the animation.
- Parameter options: Options to pass to the animation.
- Parameter animations: An animation block to execute on each view's animation.
- Parameter completion: A completion block to execute on each view's animation.
Handler to toggle the Menu open or close.
- Parameter button: A UIButton.
*/
open func close(duration: TimeInterval = 0.15, delay: TimeInterval = 0, usingSpringWithDamping: CGFloat = 0.5, initialSpringVelocity: CGFloat = 0, options: UIViewAnimationOptions = [], animations: ((UIView) -> Void)? = nil, completion: ((UIView) -> Void)? = nil) {
spring.contract(duration: duration, delay: delay, usingSpringWithDamping: usingSpringWithDamping, initialSpringVelocity: initialSpringVelocity, options: options, animations: animations, completion: completion)
@objc
fileprivate func handleToggleMenu(button: UIButton) {
guard isOpened else {
open()
return
}
close()
}
}
......@@ -30,6 +30,11 @@
import UIKit
public enum MenuBacking {
case fade
case blur
}
extension UIViewController {
/**
A convenience property that provides access to the MenuController.
......@@ -53,6 +58,15 @@ open class MenuController: RootController {
@IBInspectable
open let menu = Menu()
/// A MenuBacking value type.
open var menuBacking = MenuBacking.blur
/// The menuBacking UIBlurEffectStyle.
open var menuBackingBlurEffectStyle = UIBlurEffectStyle.light
/// A reference to the blurView.
open fileprivate(set) var blurView: UIView?
open override func layoutSubviews() {
super.layoutSubviews()
rootViewController.view.frame = view.bounds
......@@ -72,61 +86,112 @@ open class MenuController: RootController {
}
extension MenuController {
/// Prepares the Menu.
/// Prepares the menu.
fileprivate func prepareMenu() {
menu.delegate = self
menu.zPosition = 1000
view.addSubview(menu)
}
}
extension MenuController {
/**
Opens the menu with a callback.
- Parameter completion: An Optional callback that is executed when
all menu items have been opened.
*/
open func openMenu(completion: ((UIView) -> Void)? = nil) {
if true == isUserInteractionEnabled {
isUserInteractionEnabled = false
UIView.animate(withDuration: 0.15, animations: { [weak self] in
guard let s = self else {
return
}
s.rootViewController.view.alpha = 0.15
})
menu.open(usingSpringWithDamping: 0) { [completion = completion] (view) in
completion?(view)
}
/// Shows the menuBacking.
fileprivate func showFabMenuBacking() {
showFade()
showBlurView()
}
/// Hides the menuBacking.
fileprivate func hideFabMenuBacking() {
hideFade()
hideBlurView()
}
/// Shows the blurView.
fileprivate func showBlurView() {
guard .blur == menuBacking else {
return
}
guard !menu.isOpened, menu.isEnabled else {
return
}
guard nil == blurView else {
return
}
let blur = UIVisualEffectView(effect: UIBlurEffect(style: menuBackingBlurEffectStyle))
blurView = UIView()
blurView?.layout(blur).edges()
view.layout(blurView!).edges()
view.bringSubview(toFront: menu)
}
/**
Opens the menu with a callback.
- Parameter completion: An Optional callback that is executed when
all menu items have been closed.
*/
open func closeMenu(completion: ((UIView) -> Void)? = nil) {
if false == isUserInteractionEnabled {
UIView.animate(withDuration: 0.15, animations: { [weak self] in
guard let s = self else {
return
}
s.rootViewController.view.alpha = 1
})
menu.close(usingSpringWithDamping: 0) { [weak self] (view) in
guard let s = self else {
return
}
completion?(view)
if view == s.menu.views.last {
s.isUserInteractionEnabled = true
}
}
/// Hides the blurView.
fileprivate func hideBlurView() {
guard menu.isOpened, menu.isEnabled else {
return
}
blurView?.removeFromSuperview()
blurView = nil
}
/// Shows the fade.
fileprivate func showFade() {
guard .fade == menuBacking else {
return
}
guard !menu.isOpened, menu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 0.15
})
}
/// Hides the fade.
fileprivate func hideFade() {
guard menu.isOpened, menu.isEnabled else {
return
}
UIView.animate(withDuration: 0.15, animations: { [weak self] in
self?.rootViewController.view.alpha = 1
})
}
}
extension MenuController: MenuDelegate {
@objc
open func menuWillOpen(menu: Menu) {
isUserInteractionEnabled = false
showFabMenuBacking()
}
@objc
open func menuDidOpen(menu: Menu) {
isUserInteractionEnabled = true
}
@objc
open func menuWillClose(menu: Menu) {
isUserInteractionEnabled = false
hideFabMenuBacking()
}
@objc
open func menuDidClose(menu: Menu) {
isUserInteractionEnabled = true
}
@objc
open func menu(menu: Menu, tappedAt point: CGPoint, isOutside: Bool) {
guard isOutside else {
return
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment