Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
144
iphone/Maps/UI/AvailableArea/AvailableArea.swift
Normal file
144
iphone/Maps/UI/AvailableArea/AvailableArea.swift
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
class AvailableArea: UIView {
|
||||
private enum Const {
|
||||
static let observeKeyPath = "sublayers"
|
||||
}
|
||||
|
||||
var deferNotification: Bool { return true }
|
||||
|
||||
private(set) var orientation = UIDeviceOrientation.unknown {
|
||||
didSet {
|
||||
scheduleNotification()
|
||||
}
|
||||
}
|
||||
|
||||
var shouldUpdateAreaFrame: Bool {
|
||||
if let insets = UIApplication.shared.delegate?.window??.safeAreaInsets {
|
||||
return insets.left > 0 || insets.right > 0
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var areaFrame: CGRect {
|
||||
return alternative(iPhone: {
|
||||
var frame = self.frame
|
||||
if self.shouldUpdateAreaFrame {
|
||||
switch self.orientation {
|
||||
case .landscapeLeft:
|
||||
frame.origin.x -= 16
|
||||
frame.size.width += 60
|
||||
case .landscapeRight:
|
||||
frame.origin.x -= 44
|
||||
frame.size.width += 60
|
||||
default: break
|
||||
}
|
||||
}
|
||||
return frame
|
||||
}, iPad: { self.frame })()
|
||||
}
|
||||
|
||||
private var affectingViews = Set<UIView>()
|
||||
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
subscribe()
|
||||
update()
|
||||
}
|
||||
|
||||
deinit {
|
||||
unsubscribe()
|
||||
}
|
||||
|
||||
private func subscribe() {
|
||||
guard let ol = superview?.layer else { return }
|
||||
ol.addObserver(self, forKeyPath: Const.observeKeyPath, options: .new, context: nil)
|
||||
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
||||
|
||||
let nc = NotificationCenter.default
|
||||
nc.addObserver(forName: UIDevice.orientationDidChangeNotification, object: nil, queue: .main) { _ in
|
||||
let orientation = UIDevice.current.orientation
|
||||
guard !orientation.isFlat && orientation != .portraitUpsideDown else { return }
|
||||
self.orientation = orientation
|
||||
}
|
||||
}
|
||||
|
||||
private func unsubscribe() {
|
||||
guard let ol = superview?.layer else { return }
|
||||
ol.removeObserver(self, forKeyPath: Const.observeKeyPath)
|
||||
UIDevice.current.endGeneratingDeviceOrientationNotifications()
|
||||
}
|
||||
|
||||
override func observeValue(forKeyPath keyPath: String?, of _: Any?, change _: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) {
|
||||
if keyPath == Const.observeKeyPath {
|
||||
DispatchQueue.main.async {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
scheduleNotification()
|
||||
}
|
||||
|
||||
private func newAffectingViews(view: UIView) -> Set<UIView> {
|
||||
var views = Set<UIView>()
|
||||
if isAreaAffectingView(view) {
|
||||
views.insert(view)
|
||||
}
|
||||
view.subviews.forEach {
|
||||
views.formUnion(newAffectingViews(view: $0))
|
||||
}
|
||||
return views
|
||||
}
|
||||
|
||||
private func update() {
|
||||
guard let sv = superview else { return }
|
||||
let newAVs = newAffectingViews(view: sv)
|
||||
newAVs.subtracting(affectingViews).forEach(addAffectingView)
|
||||
affectingViews = newAVs
|
||||
scheduleNotification()
|
||||
}
|
||||
|
||||
func addConstraints(otherView: UIView, directions: MWMAvailableAreaAffectDirections) {
|
||||
guard !directions.isEmpty else {
|
||||
LOG(.warning, "Attempt to add empty affecting directions from \(otherView) to \(self)")
|
||||
return
|
||||
}
|
||||
let add = { (sa: NSLayoutConstraint.Attribute, oa: NSLayoutConstraint.Attribute, rel: NSLayoutConstraint.Relation) in
|
||||
let c = NSLayoutConstraint(item: self, attribute: sa, relatedBy: rel, toItem: otherView, attribute: oa, multiplier: 1, constant: 0)
|
||||
c.priority = UILayoutPriority.defaultHigh
|
||||
c.isActive = true
|
||||
}
|
||||
[
|
||||
.top: (.top, .bottom, .greaterThanOrEqual),
|
||||
.bottom: (.bottom, .top, .lessThanOrEqual),
|
||||
.left: (.left, .right, .greaterThanOrEqual),
|
||||
.right: (.right, .left, .lessThanOrEqual),
|
||||
]
|
||||
.filter { directions.contains($0.key) }
|
||||
.map { $0.value }
|
||||
.forEach(add)
|
||||
}
|
||||
|
||||
@objc
|
||||
private func scheduleNotification() {
|
||||
if deferNotification {
|
||||
let selector = #selector(notifyObserver)
|
||||
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: selector, object: nil)
|
||||
perform(selector, with: nil, afterDelay: 0)
|
||||
} else {
|
||||
notifyObserver()
|
||||
}
|
||||
}
|
||||
|
||||
func isAreaAffectingView(_: UIView) -> Bool { return false }
|
||||
func addAffectingView(_: UIView) {}
|
||||
@objc func notifyObserver() {}
|
||||
}
|
||||
|
||||
extension MWMAvailableAreaAffectDirections: Hashable {
|
||||
public var hashValue: Int {
|
||||
return rawValue
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
typedef NS_OPTIONS(NSInteger, MWMAvailableAreaAffectDirections) {
|
||||
MWMAvailableAreaAffectDirectionsNone = 0,
|
||||
MWMAvailableAreaAffectDirectionsTop = 1 << 0,
|
||||
MWMAvailableAreaAffectDirectionsBottom = 1 << 1,
|
||||
MWMAvailableAreaAffectDirectionsLeft = 1 << 2,
|
||||
MWMAvailableAreaAffectDirectionsRight = 1 << 3
|
||||
};
|
||||
21
iphone/Maps/UI/AvailableArea/NavigationInfoArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/NavigationInfoArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class NavigationInfoArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.navigationInfoAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.navigationInfoAreaAffectView
|
||||
let directions = ov.navigationInfoAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMNavigationDashboardManager.updateNavigationInfoAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
var navigationInfoAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var navigationInfoAreaAffectView: UIView { return self }
|
||||
}
|
||||
25
iphone/Maps/UI/AvailableArea/PlacePageArea.swift
Normal file
25
iphone/Maps/UI/AvailableArea/PlacePageArea.swift
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
final class PlacePageArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
return frame
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.placePageAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.placePageAreaAffectView
|
||||
let directions = ov.placePageAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMPlacePageManagerHelper.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var placePageAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var placePageAreaAffectView: UIView { return self }
|
||||
}
|
||||
23
iphone/Maps/UI/AvailableArea/SideButtonsArea.swift
Normal file
23
iphone/Maps/UI/AvailableArea/SideButtonsArea.swift
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
final class SideButtonsArea: AvailableArea {
|
||||
override var deferNotification: Bool { return false }
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.sideButtonsAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.sideButtonsAreaAffectView
|
||||
let directions = ov.sideButtonsAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMSideButtons.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var sideButtonsAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var sideButtonsAreaAffectView: UIView { return self }
|
||||
}
|
||||
29
iphone/Maps/UI/AvailableArea/TabBarArea.swift
Normal file
29
iphone/Maps/UI/AvailableArea/TabBarArea.swift
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
final class TabBarArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
var areaFrame = frame
|
||||
// Spacing is used only for devices with zero bottom safe area (such as SE).
|
||||
let additionalBottomSpacing: CGFloat = MapsAppDelegate.theApp().window.safeAreaInsets.bottom.isZero ? -10 : .zero
|
||||
areaFrame.origin.y += additionalBottomSpacing
|
||||
return areaFrame
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.tabBarAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.tabBarAreaAffectView
|
||||
let directions = ov.tabBarAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
BottomTabBarViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var tabBarAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var tabBarAreaAffectView: UIView { return self }
|
||||
}
|
||||
21
iphone/Maps/UI/AvailableArea/TrackRecordingButtonArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/TrackRecordingButtonArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class TrackRecordingButtonArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.trackRecordingButtonAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.trackRecordingButtonAreaAffectView
|
||||
let directions = ov.trackRecordingButtonAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
TrackRecordingButtonViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var trackRecordingButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var trackRecordingButtonAreaAffectView: UIView { return self }
|
||||
}
|
||||
21
iphone/Maps/UI/AvailableArea/TrafficButtonArea.swift
Normal file
21
iphone/Maps/UI/AvailableArea/TrafficButtonArea.swift
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
final class TrafficButtonArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.trafficButtonAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.trafficButtonAreaAffectView
|
||||
let directions = ov.trafficButtonAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMTrafficButtonViewController.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var trafficButtonAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var trafficButtonAreaAffectView: UIView { return self }
|
||||
}
|
||||
24
iphone/Maps/UI/AvailableArea/VisibleArea.swift
Normal file
24
iphone/Maps/UI/AvailableArea/VisibleArea.swift
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
final class VisibleArea: AvailableArea {
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.visibleAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.visibleAreaAffectView
|
||||
let directions = ov.visibleAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
if CarPlayService.shared.isCarplayActivated {
|
||||
return
|
||||
}
|
||||
FrameworkHelper.setVisibleViewport(areaFrame, scaleFactor: MapViewController.shared()?.mapView.contentScaleFactor ?? 1.0)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var visibleAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var visibleAreaAffectView: UIView { return self }
|
||||
}
|
||||
30
iphone/Maps/UI/AvailableArea/WidgetsArea.swift
Normal file
30
iphone/Maps/UI/AvailableArea/WidgetsArea.swift
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
final class WidgetsArea: AvailableArea {
|
||||
override var areaFrame: CGRect {
|
||||
return alternative(iPhone: {
|
||||
var frame = super.areaFrame
|
||||
frame.origin.y -= 16
|
||||
frame.size.height += 16
|
||||
return frame
|
||||
}, iPad: { super.areaFrame })()
|
||||
}
|
||||
|
||||
override func isAreaAffectingView(_ other: UIView) -> Bool {
|
||||
return !other.widgetsAreaAffectDirections.isEmpty
|
||||
}
|
||||
|
||||
override func addAffectingView(_ other: UIView) {
|
||||
let ov = other.widgetsAreaAffectView
|
||||
let directions = ov.widgetsAreaAffectDirections
|
||||
addConstraints(otherView: ov, directions: directions)
|
||||
}
|
||||
|
||||
override func notifyObserver() {
|
||||
MWMMapWidgetsHelper.updateAvailableArea(areaFrame)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIView {
|
||||
@objc var widgetsAreaAffectDirections: MWMAvailableAreaAffectDirections { return [] }
|
||||
|
||||
var widgetsAreaAffectView: UIView { return self }
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue