mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-30 08:12:40 +00:00
156 lines
4.7 KiB
Swift
156 lines
4.7 KiB
Swift
//
|
|
// Copyright 2022 New Vector Ltd
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import Mapbox
|
|
|
|
class UserAnnotationCalloutView: UIView, MGLCalloutView, Themable {
|
|
|
|
// MARK: - Constants
|
|
|
|
private enum Constants {
|
|
static let animationDuration: TimeInterval = 0.2
|
|
static let bottomMargin: CGFloat = 3.0
|
|
}
|
|
|
|
// MARK: - Properties
|
|
|
|
// MARK: Overrides
|
|
|
|
var representedObject: MGLAnnotation
|
|
|
|
lazy var leftAccessoryView: UIView = UIView()
|
|
|
|
lazy var rightAccessoryView: UIView = UIView()
|
|
|
|
var delegate: MGLCalloutViewDelegate?
|
|
|
|
// Allow the callout to remain open during panning.
|
|
let dismissesAutomatically: Bool = false
|
|
|
|
let isAnchoredToAnnotation: Bool = true
|
|
|
|
// https://github.com/mapbox/mapbox-gl-native/issues/9228
|
|
override var center: CGPoint {
|
|
set {
|
|
var newCenter = newValue
|
|
newCenter.y -= bounds.midY + Constants.bottomMargin
|
|
super.center = newCenter
|
|
}
|
|
get {
|
|
return super.center
|
|
}
|
|
}
|
|
|
|
// MARK: Private
|
|
|
|
lazy var contentView: UserAnnotationCalloutContentView = {
|
|
return UserAnnotationCalloutContentView.instantiate()
|
|
}()
|
|
|
|
// MARK: - Setup
|
|
|
|
required init(userLocationAnnotation: UserLocationAnnotation) {
|
|
|
|
self.representedObject = userLocationAnnotation
|
|
|
|
super.init(frame: .zero)
|
|
|
|
self.vc_addSubViewMatchingParent(self.contentView)
|
|
|
|
self.update(theme: ThemeService.shared().theme)
|
|
|
|
let size = UserAnnotationCalloutContentView.contentViewSize()
|
|
|
|
self.frame = CGRect(origin: .zero, size: size)
|
|
}
|
|
|
|
required init?(coder decoder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// MARK: - Public
|
|
|
|
func update(theme: Theme) {
|
|
self.contentView.update(theme: theme)
|
|
}
|
|
|
|
// MARK: - Overrides
|
|
|
|
func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {
|
|
|
|
// Set callout above the marker view
|
|
|
|
self.center = view.center.applying(CGAffineTransform(translationX: 0, y: view.bounds.height/2 + self.bounds.height))
|
|
|
|
delegate?.calloutViewWillAppear?(self)
|
|
|
|
view.addSubview(self)
|
|
|
|
if isCalloutTappable() {
|
|
// Handle taps and eventually try to send them to the delegate (usually the map view).
|
|
self.contentView.shareButton.addTarget(self, action: #selector(UserAnnotationCalloutView.calloutTapped), for: .touchUpInside)
|
|
} else {
|
|
// Disable tapping and highlighting.
|
|
self.contentView.shareButton.isUserInteractionEnabled = false
|
|
}
|
|
|
|
if animated {
|
|
alpha = 0
|
|
|
|
UIView.animate(withDuration: Constants.animationDuration) { [weak self] in
|
|
guard let strongSelf = self else {
|
|
return
|
|
}
|
|
|
|
strongSelf.alpha = 1
|
|
strongSelf.delegate?.calloutViewDidAppear?(strongSelf)
|
|
}
|
|
} else {
|
|
delegate?.calloutViewDidAppear?(self)
|
|
}
|
|
}
|
|
|
|
func dismissCallout(animated: Bool) {
|
|
if (superview != nil) {
|
|
if animated {
|
|
UIView.animate(withDuration: Constants.animationDuration, animations: { [weak self] in
|
|
self?.alpha = 0
|
|
}, completion: { [weak self] _ in
|
|
self?.removeFromSuperview()
|
|
})
|
|
} else {
|
|
removeFromSuperview()
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Callout interaction handlers
|
|
|
|
func isCalloutTappable() -> Bool {
|
|
if let delegate = delegate {
|
|
if delegate.responds(to: #selector(MGLCalloutViewDelegate.calloutViewShouldHighlight)) {
|
|
return delegate.calloutViewShouldHighlight!(self)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
@objc func calloutTapped() {
|
|
if isCalloutTappable() && delegate!.responds(to: #selector(MGLCalloutViewDelegate.calloutViewTapped)) {
|
|
delegate!.calloutViewTapped!(self)
|
|
}
|
|
}
|
|
}
|