mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-02 06:06:57 +02:00
Move RoomTimelineLocationView to appropriate folder.
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// Copyright 2021 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 UIKit
|
||||
import Reusable
|
||||
import Mapbox
|
||||
|
||||
class LocationMarkerView: MGLAnnotationView, NibLoadable {
|
||||
|
||||
@IBOutlet private var backgroundImageView: UIImageView!
|
||||
@IBOutlet private var avatarView: UserAvatarView!
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
}
|
||||
|
||||
func setAvatarData(_ avatarData: AvatarViewDataProtocol, avatarBackgroundColor: UIColor) {
|
||||
backgroundImageView.image = Asset.Images.locationUserMarker.image
|
||||
backgroundImageView.tintColor = avatarBackgroundColor
|
||||
avatarView.fill(with: avatarData)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iN0-l3-epB" customClass="LocationMarkerView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="42" height="85"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_user_marker" translatesAutoresizingMaskIntoConstraints="NO" id="ldO-kc-R5W">
|
||||
<rect key="frame" x="0.0" y="0.0" width="42" height="45"/>
|
||||
<color key="tintColor" red="0.050980392159999999" green="0.74117647060000003" blue="0.5450980392" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="42" id="R2b-uu-zTh"/>
|
||||
<constraint firstAttribute="height" constant="45" id="kEu-X4-Zv7"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_marker_icon" translatesAutoresizingMaskIntoConstraints="NO" id="gQe-Hv-22e">
|
||||
<rect key="frame" x="9" y="9.5" width="24" height="24"/>
|
||||
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="XuF-VU-qG2"/>
|
||||
<constraint firstAttribute="height" constant="24" id="ejE-pC-umv"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="qut-wn-BX3" customClass="UserAvatarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="3" y="3.5" width="36" height="36"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="BjG-I5-n8f"/>
|
||||
<constraint firstAttribute="width" constant="36" id="W3F-Aw-FO3"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="KWg-z1-qEd" userLabel="Offset View">
|
||||
<rect key="frame" x="0.0" y="45" width="42" height="40"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="KWg-z1-qEd" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="0py-1i-Gdf"/>
|
||||
<constraint firstItem="ldO-kc-R5W" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="5a8-E1-MIa"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ldO-kc-R5W" secondAttribute="trailing" id="Lln-IS-hLl"/>
|
||||
<constraint firstItem="qut-wn-BX3" firstAttribute="centerX" secondItem="ldO-kc-R5W" secondAttribute="centerX" id="TUG-aN-usd"/>
|
||||
<constraint firstItem="KWg-z1-qEd" firstAttribute="height" secondItem="ldO-kc-R5W" secondAttribute="height" constant="-5" id="TlE-3X-R0r"/>
|
||||
<constraint firstItem="KWg-z1-qEd" firstAttribute="top" secondItem="ldO-kc-R5W" secondAttribute="bottom" id="WYZ-ob-djk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="KWg-z1-qEd" secondAttribute="bottom" id="Zze-LI-fsw"/>
|
||||
<constraint firstItem="gQe-Hv-22e" firstAttribute="centerY" secondItem="ldO-kc-R5W" secondAttribute="centerY" constant="-1" id="a7A-n2-rUT"/>
|
||||
<constraint firstAttribute="trailing" secondItem="KWg-z1-qEd" secondAttribute="trailing" id="ayz-pJ-gK1"/>
|
||||
<constraint firstItem="KWg-z1-qEd" firstAttribute="width" secondItem="ldO-kc-R5W" secondAttribute="width" id="hy9-K6-5Ss"/>
|
||||
<constraint firstItem="gQe-Hv-22e" firstAttribute="centerX" secondItem="ldO-kc-R5W" secondAttribute="centerX" id="iSa-mh-J7b"/>
|
||||
<constraint firstItem="ldO-kc-R5W" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="vFi-tw-CqT"/>
|
||||
<constraint firstItem="qut-wn-BX3" firstAttribute="centerY" secondItem="ldO-kc-R5W" secondAttribute="centerY" constant="-1" id="xoi-mp-qC5"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="avatarView" destination="qut-wn-BX3" id="wHA-bz-A2y"/>
|
||||
<outlet property="backgroundImageView" destination="ldO-kc-R5W" id="52a-Fs-iu7"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="58.695652173913047" y="4.6875"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="location_marker_icon" width="24" height="24"/>
|
||||
<image name="location_user_marker" width="42" height="45"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,417 @@
|
||||
//
|
||||
// Copyright 2021 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 UIKit
|
||||
import Reusable
|
||||
import Mapbox
|
||||
import SwiftUI
|
||||
|
||||
protocol RoomTimelineLocationViewDelegate: AnyObject {
|
||||
func roomTimelineLocationViewDidTapStopButton(_ roomTimelineLocationView: RoomTimelineLocationView)
|
||||
func roomTimelineLocationViewDidTapRetryButton(_ roomTimelineLocationView: RoomTimelineLocationView)
|
||||
}
|
||||
|
||||
struct RoomTimelineLocationViewData {
|
||||
let location: CLLocationCoordinate2D?
|
||||
let userAvatarData: AvatarViewData?
|
||||
let mapStyleURL: URL
|
||||
}
|
||||
|
||||
struct TimelineLiveLocationViewData {
|
||||
let status: LiveLocationSharingStatus
|
||||
let iconTint: UIColor
|
||||
let title: String
|
||||
let titleColor: UIColor
|
||||
let timeLeftString: String?
|
||||
let rightButtonTitle: String?
|
||||
let rightButtonTag: RightButtonTag
|
||||
let coordinate: CLLocationCoordinate2D?
|
||||
|
||||
var showTimer: Bool {
|
||||
return timeLeftString != nil
|
||||
}
|
||||
|
||||
var showRightButton: Bool {
|
||||
return rightButtonTitle != nil
|
||||
}
|
||||
|
||||
var showMap: Bool {
|
||||
guard case .started = status else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
enum TimelineLiveLocationViewState {
|
||||
case incoming(_ status: LiveLocationSharingStatus) // live location started by other users
|
||||
case outgoing(_ status: LiveLocationSharingStatus) // live location started from current user
|
||||
}
|
||||
|
||||
|
||||
enum LiveLocationSharingStatus {
|
||||
case starting
|
||||
case started(_ coordinate: CLLocationCoordinate2D, _ timeleft: TimeInterval)
|
||||
case failure
|
||||
case stopped
|
||||
}
|
||||
|
||||
enum RightButtonTag: Int {
|
||||
case stopSharing = 0
|
||||
case retrySharing
|
||||
}
|
||||
|
||||
class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegate {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private struct Constants {
|
||||
static let mapHeight: CGFloat = 300.0
|
||||
static let mapZoomLevel = 15.0
|
||||
static let cellBorderRadius: CGFloat = 1.0
|
||||
static let cellCornerRadius: CGFloat = 8.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
// MARK: Private
|
||||
|
||||
@IBOutlet private var descriptionContainerView: UIView!
|
||||
@IBOutlet private var descriptionLabel: UILabel!
|
||||
@IBOutlet private var descriptionIcon: UIImageView!
|
||||
@IBOutlet private var attributionLabel: UILabel!
|
||||
|
||||
// MARK: - Live Location
|
||||
@IBOutlet private var placeholderBackground: UIImageView!
|
||||
@IBOutlet private var placeholderIconView: UIImageView!
|
||||
@IBOutlet private var liveLocationContainerView: UIView!
|
||||
@IBOutlet private var liveLocationIcon: UIImageView!
|
||||
@IBOutlet private var liveLocationIconBackgroundView: UIView!
|
||||
@IBOutlet private var liveLocationStatusLabel: UILabel!
|
||||
@IBOutlet private var liveLocationTimerLabel: UILabel!
|
||||
@IBOutlet private var rightButton: UIButton!
|
||||
@IBOutlet private var activityIndicatorView: UIActivityIndicatorView!
|
||||
|
||||
@IBOutlet private var mapLoadingErrorContainerView: UIView!
|
||||
@IBOutlet private var mapLoadingErrorImageView: UIImageView!
|
||||
@IBOutlet private var mapLoadingErrorMessageLabel: UILabel!
|
||||
|
||||
private var mapView: MGLMapView!
|
||||
|
||||
private var isMapViewLoadingFailed: Bool = false {
|
||||
didSet {
|
||||
if oldValue != isMapViewLoadingFailed {
|
||||
self.mapViewLoadingStateDidChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
private var annotationView: LocationMarkerView?
|
||||
private static var usernameColorGenerator = UserNameColorGenerator()
|
||||
private var theme: Theme!
|
||||
private var placeholderBackgroundImage: UIImage?
|
||||
private var placeholderIcon: UIImage?
|
||||
|
||||
private lazy var incomingTimerFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "HH:mm"
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
private lazy var outgoingTimerFormatter: DateComponentsFormatter = {
|
||||
let formatter = DateComponentsFormatter()
|
||||
formatter.zeroFormattingBehavior = .dropAll
|
||||
formatter.allowedUnits = [.hour, .minute, .second]
|
||||
formatter.unitsStyle = .brief
|
||||
return formatter
|
||||
}()
|
||||
|
||||
weak var delegate: RoomTimelineLocationViewDelegate?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var locationDescription: String? {
|
||||
get {
|
||||
descriptionLabel.text
|
||||
}
|
||||
set {
|
||||
descriptionLabel.text = newValue
|
||||
descriptionContainerView.isHidden = (newValue?.count ?? 0 == 0)
|
||||
}
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
mapView = MGLMapView(frame: .zero)
|
||||
mapView.delegate = self
|
||||
mapView.logoView.isHidden = true
|
||||
mapView.attributionButton.isHidden = true
|
||||
mapView.isUserInteractionEnabled = false
|
||||
|
||||
mapView.translatesAutoresizingMaskIntoConstraints = false
|
||||
mapView.addConstraint(mapView.heightAnchor.constraint(equalToConstant: Constants.mapHeight))
|
||||
vc_addSubViewMatchingParent(mapView)
|
||||
sendSubviewToBack(mapView)
|
||||
|
||||
clipsToBounds = true
|
||||
layer.borderWidth = Constants.cellBorderRadius
|
||||
layer.cornerRadius = Constants.cellCornerRadius
|
||||
|
||||
mapLoadingErrorContainerView.isHidden = true
|
||||
mapLoadingErrorImageView.image = Asset.Images.locationMapError.image
|
||||
mapLoadingErrorMessageLabel.text = VectorL10n.locationSharingMapLoadingError
|
||||
|
||||
theme = ThemeService.shared().theme
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func resetMapViewLoadingState() {
|
||||
self.isMapViewLoadingFailed = false
|
||||
}
|
||||
|
||||
private func mapViewLoadingStateDidChange() {
|
||||
|
||||
if mapView.isHidden == false && self.isMapViewLoadingFailed {
|
||||
mapLoadingErrorContainerView.isHidden = false
|
||||
mapView.isHidden = true
|
||||
attributionLabel.isHidden = true
|
||||
} else {
|
||||
mapLoadingErrorContainerView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
private func displayLocation(_ location: CLLocationCoordinate2D?,
|
||||
userAvatarData: AvatarViewData? = nil,
|
||||
mapStyleURL: URL,
|
||||
bannerViewData: TimelineLiveLocationViewData? = nil) {
|
||||
|
||||
resetMapViewLoadingState()
|
||||
|
||||
if let location = location {
|
||||
mapView.isHidden = false
|
||||
mapView.styleURL = mapStyleURL
|
||||
|
||||
annotationView = LocationMarkerView.loadFromNib()
|
||||
|
||||
if let userAvatarData = userAvatarData {
|
||||
let avatarBackgroundColor = Self.usernameColorGenerator.color(from: userAvatarData.matrixItemId)
|
||||
annotationView?.setAvatarData(userAvatarData, avatarBackgroundColor: avatarBackgroundColor)
|
||||
}
|
||||
|
||||
if let annotations = mapView.annotations {
|
||||
mapView.removeAnnotations(annotations)
|
||||
}
|
||||
|
||||
mapView.setCenter(location, zoomLevel: Constants.mapZoomLevel, animated: false)
|
||||
|
||||
let pointAnnotation = MGLPointAnnotation()
|
||||
pointAnnotation.coordinate = location
|
||||
mapView.addAnnotation(pointAnnotation)
|
||||
} else {
|
||||
mapView.isHidden = true
|
||||
}
|
||||
|
||||
// Configure live location banner
|
||||
guard let bannerViewData = bannerViewData else {
|
||||
liveLocationContainerView.isHidden = true
|
||||
placeholderBackground.isHidden = true
|
||||
placeholderIconView.isHidden = true
|
||||
return
|
||||
}
|
||||
|
||||
liveLocationContainerView.isHidden = false
|
||||
liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.90)
|
||||
|
||||
liveLocationIcon.image = Asset.Images.locationLiveCellIcon.image
|
||||
liveLocationIcon.tintColor = bannerViewData.iconTint
|
||||
liveLocationIconBackgroundView.isHidden = !bannerViewData.showMap // Add white background when cell is not in starting or ended state
|
||||
|
||||
liveLocationStatusLabel.text = bannerViewData.title
|
||||
liveLocationStatusLabel.textColor = bannerViewData.titleColor
|
||||
|
||||
liveLocationTimerLabel.text = bannerViewData.timeLeftString
|
||||
liveLocationTimerLabel.textColor = theme.colors.tertiaryContent
|
||||
liveLocationTimerLabel.isHidden = !bannerViewData.showTimer
|
||||
|
||||
rightButton.setTitle(bannerViewData.rightButtonTitle, for: .normal)
|
||||
rightButton.isHidden = !bannerViewData.showRightButton
|
||||
rightButton.tag = bannerViewData.rightButtonTag.rawValue
|
||||
|
||||
placeholderBackground.isHidden = bannerViewData.showMap
|
||||
placeholderIconView.image = placeholderIcon
|
||||
placeholderIconView.isHidden = bannerViewData.showMap
|
||||
placeholderBackground.isHidden = bannerViewData.showMap
|
||||
placeholderBackground.image = placeholderBackgroundImage
|
||||
mapView.isHidden = !bannerViewData.showMap
|
||||
attributionLabel.isHidden = !bannerViewData.showMap
|
||||
|
||||
switch bannerViewData.status {
|
||||
case .starting:
|
||||
placeholderIconView.isHidden = true
|
||||
activityIndicatorView.isHidden = false
|
||||
activityIndicatorView.startAnimating()
|
||||
default:
|
||||
activityIndicatorView.isHidden = true
|
||||
activityIndicatorView.stopAnimating()
|
||||
}
|
||||
}
|
||||
|
||||
private func liveLocationBannerViewData(from viewState: TimelineLiveLocationViewState) -> TimelineLiveLocationViewData {
|
||||
|
||||
let status: LiveLocationSharingStatus
|
||||
let iconTint: UIColor
|
||||
let title: String
|
||||
var titleColor: UIColor = theme.colors.primaryContent
|
||||
var timeLeftString: String?
|
||||
var rightButtonTitle: String?
|
||||
var rightButtonTag: RightButtonTag = .stopSharing
|
||||
var liveCoordinate: CLLocationCoordinate2D?
|
||||
|
||||
switch viewState {
|
||||
case .incoming(let liveLocationSharingStatus):
|
||||
status = liveLocationSharingStatus
|
||||
switch liveLocationSharingStatus {
|
||||
case .starting:
|
||||
iconTint = theme.colors.quarterlyContent
|
||||
title = VectorL10n.locationSharingLiveLoading
|
||||
titleColor = theme.colors.tertiaryContent
|
||||
case .started(let coordinate, let timeLeft):
|
||||
iconTint = theme.roomCellLocalisationIconStartedColor
|
||||
title = VectorL10n.liveLocationSharingBannerTitle
|
||||
timeLeftString = generateTimerString(for: timeLeft, isIncomingLocation: true)
|
||||
liveCoordinate = coordinate
|
||||
case .failure:
|
||||
iconTint = theme.roomCellLocalisationErrorColor
|
||||
title = VectorL10n.locationSharingLiveError
|
||||
rightButtonTitle = VectorL10n.retry
|
||||
rightButtonTag = .retrySharing
|
||||
case .stopped:
|
||||
iconTint = theme.colors.quarterlyContent
|
||||
title = VectorL10n.liveLocationSharingEnded
|
||||
titleColor = theme.colors.tertiaryContent
|
||||
}
|
||||
case .outgoing(let liveLocationSharingStatus):
|
||||
status = liveLocationSharingStatus
|
||||
switch liveLocationSharingStatus {
|
||||
case .starting:
|
||||
iconTint = theme.colors.quarterlyContent
|
||||
title = VectorL10n.locationSharingLiveLoading
|
||||
titleColor = theme.colors.tertiaryContent
|
||||
case .started(let coordinate, let timeLeft):
|
||||
iconTint = theme.roomCellLocalisationIconStartedColor
|
||||
title = VectorL10n.liveLocationSharingBannerTitle
|
||||
timeLeftString = generateTimerString(for: timeLeft, isIncomingLocation: false)
|
||||
rightButtonTitle = VectorL10n.stop
|
||||
liveCoordinate = coordinate
|
||||
case .failure:
|
||||
iconTint = theme.roomCellLocalisationErrorColor
|
||||
title = VectorL10n.locationSharingLiveError
|
||||
rightButtonTitle = VectorL10n.retry
|
||||
rightButtonTag = .retrySharing
|
||||
case .stopped:
|
||||
iconTint = theme.colors.quarterlyContent
|
||||
title = VectorL10n.liveLocationSharingEnded
|
||||
titleColor = theme.colors.tertiaryContent
|
||||
}
|
||||
}
|
||||
|
||||
return TimelineLiveLocationViewData(status: status,
|
||||
iconTint: iconTint,
|
||||
title: title,
|
||||
titleColor: titleColor,
|
||||
timeLeftString: timeLeftString,
|
||||
rightButtonTitle: rightButtonTitle,
|
||||
rightButtonTag: rightButtonTag,
|
||||
coordinate: liveCoordinate)
|
||||
}
|
||||
|
||||
private func generateTimerString(for timestamp: Double,
|
||||
isIncomingLocation: Bool) -> String? {
|
||||
let timerInSec = timestamp
|
||||
let timerString: String?
|
||||
if isIncomingLocation {
|
||||
timerString = VectorL10n.locationSharingLiveTimerIncoming(incomingTimerFormatter.string(from: Date(timeIntervalSince1970: timerInSec)))
|
||||
} else if let outgoingTimer = outgoingTimerFormatter.string(from: Date(timeIntervalSince1970: timerInSec).timeIntervalSinceNow) {
|
||||
timerString = VectorL10n.locationSharingLiveListItemTimeLeft(outgoingTimer)
|
||||
} else {
|
||||
timerString = nil
|
||||
}
|
||||
return timerString
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
public func displayStaticLocation(with viewData: RoomTimelineLocationViewData) {
|
||||
displayLocation(viewData.location,
|
||||
userAvatarData: viewData.userAvatarData,
|
||||
mapStyleURL: viewData.mapStyleURL,
|
||||
bannerViewData: nil)
|
||||
}
|
||||
|
||||
public func displayLiveLocation(with viewData: RoomTimelineLocationViewData, liveLocationViewState: TimelineLiveLocationViewState) {
|
||||
let bannerViewData = liveLocationBannerViewData(from: liveLocationViewState)
|
||||
displayLocation(bannerViewData.coordinate,
|
||||
userAvatarData: viewData.userAvatarData,
|
||||
mapStyleURL: viewData.mapStyleURL,
|
||||
bannerViewData: bannerViewData)
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Themable
|
||||
|
||||
func update(theme: Theme) {
|
||||
Self.usernameColorGenerator.update(theme: theme)
|
||||
descriptionLabel.textColor = theme.colors.primaryContent
|
||||
descriptionLabel.font = theme.fonts.footnote
|
||||
descriptionIcon.tintColor = theme.colors.accent
|
||||
attributionLabel.textColor = theme.colors.accent
|
||||
layer.borderColor = theme.colors.quinaryContent.cgColor
|
||||
self.theme = theme
|
||||
placeholderIcon = ThemeService.shared().isCurrentThemeDark() ? Asset.Images.locationLiveCellEndedDarkIcon.image : Asset.Images.locationLiveCellEndedLightIcon.image
|
||||
placeholderBackgroundImage = ThemeService.shared().isCurrentThemeDark() ? Asset.Images.locationBackgroundDarkImage.image : Asset.Images.locationBackgroundLightImage.image
|
||||
|
||||
mapLoadingErrorContainerView.backgroundColor = theme.colors.system
|
||||
mapLoadingErrorMessageLabel.textColor = theme.colors.primaryContent
|
||||
}
|
||||
|
||||
// MARK: - MGLMapViewDelegate
|
||||
|
||||
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
|
||||
return annotationView
|
||||
}
|
||||
|
||||
func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {
|
||||
|
||||
MXLog.error("[RoomTimelineLocationView] Failed to load map with error: \(error)")
|
||||
|
||||
self.isMapViewLoadingFailed = true
|
||||
}
|
||||
|
||||
func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {
|
||||
self.isMapViewLoadingFailed = false
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
@IBAction private func didTapTightButton(_ sender: Any) {
|
||||
if rightButton.tag == RightButtonTag.stopSharing.rawValue {
|
||||
delegate?.roomTimelineLocationViewDidTapStopButton(self)
|
||||
} else if rightButton.tag == RightButtonTag.retrySharing.rawValue {
|
||||
delegate?.roomTimelineLocationViewDidTapRetryButton(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="RoomTimelineLocationView" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="250"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_background_light_image" translatesAutoresizingMaskIntoConstraints="NO" id="vuV-BV-bbx" userLabel="Placeholder BackGround Image">
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="250"/>
|
||||
</imageView>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_live_cell_ended_light_icon" translatesAutoresizingMaskIntoConstraints="NO" id="4WL-gT-HBV">
|
||||
<rect key="frame" x="175" y="67.5" width="45" height="65"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="65" id="c7D-e5-dkZ"/>
|
||||
<constraint firstAttribute="width" constant="45" id="q3h-Mg-lG4"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XkV-iZ-sfD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="250"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4gW-5c-wLy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="182"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NlS-uE-OrS">
|
||||
<rect key="frame" x="50.5" y="52" width="294.5" height="78.5"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_map_error" translatesAutoresizingMaskIntoConstraints="NO" id="ror-cm-dP7">
|
||||
<rect key="frame" x="131" y="5" width="32" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="32" id="IpE-8Q-U1X"/>
|
||||
<constraint firstAttribute="width" constant="32" id="diE-aQ-iGF"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Eml-nq-quj">
|
||||
<rect key="frame" x="15" y="47" width="264.5" height="26.5"/>
|
||||
<string key="text">Unable to load map
|
||||
This homeserver is not configured to display maps</string>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Eml-nq-quj" secondAttribute="trailing" constant="15" id="J2n-U6-RWG"/>
|
||||
<constraint firstItem="ror-cm-dP7" firstAttribute="centerX" secondItem="NlS-uE-OrS" secondAttribute="centerX" id="MtV-hn-HJI"/>
|
||||
<constraint firstItem="Eml-nq-quj" firstAttribute="top" secondItem="ror-cm-dP7" secondAttribute="bottom" constant="10" id="RSS-ta-Ycc"/>
|
||||
<constraint firstItem="Eml-nq-quj" firstAttribute="leading" secondItem="NlS-uE-OrS" secondAttribute="leading" constant="15" id="SJs-dd-Xp2"/>
|
||||
<constraint firstAttribute="width" priority="250" id="bQp-M0-ad4"/>
|
||||
<constraint firstAttribute="height" priority="250" id="pgg-Tl-wNT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Eml-nq-quj" secondAttribute="bottom" constant="5" id="qZ2-9M-Jha"/>
|
||||
<constraint firstItem="ror-cm-dP7" firstAttribute="top" secondItem="NlS-uE-OrS" secondAttribute="top" constant="5" id="uB2-Ry-jUz"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NlS-uE-OrS" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="4gW-5c-wLy" secondAttribute="leading" id="D4Y-Jn-aKa"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="NlS-uE-OrS" secondAttribute="bottom" id="GZv-Aw-Qdq"/>
|
||||
<constraint firstItem="NlS-uE-OrS" firstAttribute="top" relation="greaterThanOrEqual" secondItem="4gW-5c-wLy" secondAttribute="top" id="QnJ-vE-nWS"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="NlS-uE-OrS" secondAttribute="trailing" id="h7C-La-6Dc"/>
|
||||
<constraint firstItem="NlS-uE-OrS" firstAttribute="centerY" secondItem="4gW-5c-wLy" secondAttribute="centerY" id="k3F-mp-2pw"/>
|
||||
<constraint firstItem="NlS-uE-OrS" firstAttribute="centerX" secondItem="4gW-5c-wLy" secondAttribute="centerX" id="rfi-Zz-UfZ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="4gW-5c-wLy" firstAttribute="leading" secondItem="XkV-iZ-sfD" secondAttribute="leading" id="6Yv-CA-oUv"/>
|
||||
<constraint firstAttribute="trailing" secondItem="4gW-5c-wLy" secondAttribute="trailing" id="jge-Zc-VzB"/>
|
||||
<constraint firstItem="4gW-5c-wLy" firstAttribute="top" secondItem="XkV-iZ-sfD" secondAttribute="top" id="ojd-BO-iRA"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="0D1-Km-vTu">
|
||||
<rect key="frame" x="0.0" y="182" width="395" height="68"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="HzR-Av-TiG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="395" height="20"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" insetsLayoutMarginsFromSafeArea="NO" text="©MapTiler ©OpenStreetMap contributors" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jWW-0w-1YM">
|
||||
<rect key="frame" x="8" y="0.0" width="379" height="20"/>
|
||||
<edgeInsets key="layoutMargins" top="0.0" left="0.0" bottom="0.0" right="0.0"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="10"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="jWW-0w-1YM" firstAttribute="top" secondItem="HzR-Av-TiG" secondAttribute="top" id="1Hd-f0-Is6"/>
|
||||
<constraint firstItem="jWW-0w-1YM" firstAttribute="leading" secondItem="HzR-Av-TiG" secondAttribute="leading" constant="8" id="R9R-Za-1Li"/>
|
||||
<constraint firstAttribute="trailing" secondItem="jWW-0w-1YM" secondAttribute="trailing" constant="8" id="Up7-yC-9tX"/>
|
||||
<constraint firstAttribute="height" constant="20" id="ZuB-fS-sSr"/>
|
||||
<constraint firstAttribute="bottom" secondItem="jWW-0w-1YM" secondAttribute="bottom" id="t8L-m8-q4c"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Hpx-8P-nCb" userLabel="Live Location Container View">
|
||||
<rect key="frame" x="0.0" y="20" width="395" height="48"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="72o-VM-Ls7" userLabel="Icon Background View">
|
||||
<rect key="frame" x="11" y="15" width="26" height="18"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_live_cell_icon" translatesAutoresizingMaskIntoConstraints="NO" id="pgy-lK-ehN">
|
||||
<rect key="frame" x="8" y="8" width="32" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="32" id="BxH-3l-2Gp"/>
|
||||
<constraint firstAttribute="height" constant="32" id="yBz-Vf-p70"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="JPw-HF-QCt">
|
||||
<rect key="frame" x="48" y="8" width="120" height="32"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Live location enabled" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="X9R-EV-RDT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="120" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="rSI-Hk-JXA"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.090196078430000007" green="0.098039215690000001" blue="0.10980392160000001" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="9min left" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="U75-c5-got">
|
||||
<rect key="frame" x="0.0" y="16" width="120" height="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.45098039215686275" green="0.49019607843137253" blue="0.5490196078431373" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="trailing" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="k5H-UE-ygH">
|
||||
<rect key="frame" x="178" y="0.0" width="209" height="48"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||
<color key="tintColor" red="1" green="0.35686274509999999" blue="0.33333333329999998" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
|
||||
<state key="normal" title="Stop">
|
||||
<color key="titleColor" red="1" green="0.35686274509999999" blue="0.33333333329999998" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="didTapTightButton:" destination="iN0-l3-epB" eventType="touchUpInside" id="bFp-df-OEs"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="pgy-lK-ehN" firstAttribute="leading" secondItem="Hpx-8P-nCb" secondAttribute="leading" constant="8" id="01K-ir-lbV"/>
|
||||
<constraint firstAttribute="bottom" secondItem="k5H-UE-ygH" secondAttribute="bottom" id="3V9-Tx-37F"/>
|
||||
<constraint firstItem="k5H-UE-ygH" firstAttribute="top" secondItem="Hpx-8P-nCb" secondAttribute="top" id="3lY-kF-sbf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="pgy-lK-ehN" secondAttribute="bottom" constant="8" id="627-HR-qTF"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k5H-UE-ygH" secondAttribute="trailing" constant="8" id="7tR-A3-1wz"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="trailing" secondItem="pgy-lK-ehN" secondAttribute="trailing" constant="-3" id="GzI-KY-kl5"/>
|
||||
<constraint firstItem="k5H-UE-ygH" firstAttribute="centerY" secondItem="Hpx-8P-nCb" secondAttribute="centerY" id="UZb-Pi-ehQ"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="centerY" secondItem="pgy-lK-ehN" secondAttribute="centerY" id="VSj-Of-cMQ"/>
|
||||
<constraint firstItem="JPw-HF-QCt" firstAttribute="centerY" secondItem="Hpx-8P-nCb" secondAttribute="centerY" id="Z6p-7l-ACV"/>
|
||||
<constraint firstItem="JPw-HF-QCt" firstAttribute="leading" secondItem="pgy-lK-ehN" secondAttribute="trailing" constant="8" id="f4u-0a-3Gt"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="top" secondItem="pgy-lK-ehN" secondAttribute="top" constant="7" id="jJ1-WV-ihb"/>
|
||||
<constraint firstItem="k5H-UE-ygH" firstAttribute="leading" secondItem="JPw-HF-QCt" secondAttribute="trailing" constant="10" id="kEW-C8-xxU"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="leading" secondItem="pgy-lK-ehN" secondAttribute="leading" constant="3" id="mm0-gW-auW"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="bottom" secondItem="pgy-lK-ehN" secondAttribute="bottom" constant="-7" id="okg-2g-Xw4"/>
|
||||
<constraint firstItem="pgy-lK-ehN" firstAttribute="top" secondItem="Hpx-8P-nCb" secondAttribute="top" constant="8" id="p9w-O9-Dnr"/>
|
||||
<constraint firstItem="pgy-lK-ehN" firstAttribute="centerY" secondItem="Hpx-8P-nCb" secondAttribute="centerY" id="svd-Ec-FWD"/>
|
||||
<constraint firstItem="72o-VM-Ls7" firstAttribute="centerX" secondItem="pgy-lK-ehN" secondAttribute="centerX" id="w2R-lD-Ded"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oVd-gS-Rmb">
|
||||
<rect key="frame" x="0.0" y="68" width="395" height="40"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="XHz-4S-fh4">
|
||||
<rect key="frame" x="12" y="8" width="371" height="24"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="location_marker_icon" translatesAutoresizingMaskIntoConstraints="NO" id="GP2-dA-giJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="7nK-Kb-7Iq"/>
|
||||
<constraint firstAttribute="height" constant="24" id="nBW-gN-0uW"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c68-l7-McA">
|
||||
<rect key="frame" x="32" y="2" width="339" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="XHz-4S-fh4" secondAttribute="trailing" constant="12" id="FI1-B7-bPV"/>
|
||||
<constraint firstItem="XHz-4S-fh4" firstAttribute="top" secondItem="oVd-gS-Rmb" secondAttribute="top" constant="8" id="UJq-Yz-ikR"/>
|
||||
<constraint firstAttribute="bottom" secondItem="XHz-4S-fh4" secondAttribute="bottom" constant="8" id="cvr-Gb-uLe"/>
|
||||
<constraint firstItem="XHz-4S-fh4" firstAttribute="leading" secondItem="oVd-gS-Rmb" secondAttribute="leading" constant="12" id="wSE-NS-2h4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="large" translatesAutoresizingMaskIntoConstraints="NO" id="1j3-sd-BvY">
|
||||
<rect key="frame" x="175" y="77.5" width="45" height="45"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="45" id="Mc9-VR-e3H"/>
|
||||
<constraint firstAttribute="width" constant="45" id="gqh-tx-UXQ"/>
|
||||
</constraints>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="1j3-sd-BvY" firstAttribute="centerY" secondItem="4WL-gT-HBV" secondAttribute="centerY" id="A5v-W4-VaN"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vuV-BV-bbx" secondAttribute="trailing" id="Gck-1z-j8f"/>
|
||||
<constraint firstAttribute="bottom" secondItem="XkV-iZ-sfD" secondAttribute="bottom" id="LU8-RW-Azf"/>
|
||||
<constraint firstItem="vuV-BV-bbx" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="NhI-xD-L7V"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0D1-Km-vTu" secondAttribute="trailing" id="QHD-xv-nfX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="XkV-iZ-sfD" secondAttribute="trailing" id="ZdZ-d1-u47"/>
|
||||
<constraint firstItem="XkV-iZ-sfD" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="a2R-cf-5Tn"/>
|
||||
<constraint firstItem="XkV-iZ-sfD" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="c6b-kS-Dlf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="vuV-BV-bbx" secondAttribute="bottom" id="eKg-25-HDW"/>
|
||||
<constraint firstItem="0D1-Km-vTu" firstAttribute="top" secondItem="4gW-5c-wLy" secondAttribute="bottom" id="eQz-kf-RIl"/>
|
||||
<constraint firstItem="4WL-gT-HBV" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="eRF-LN-Evm"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0D1-Km-vTu" secondAttribute="bottom" id="ea5-xx-V3s"/>
|
||||
<constraint firstItem="1j3-sd-BvY" firstAttribute="centerX" secondItem="4WL-gT-HBV" secondAttribute="centerX" id="fxh-rF-fpN"/>
|
||||
<constraint firstItem="4WL-gT-HBV" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" constant="-25" id="nKg-p6-0mc"/>
|
||||
<constraint firstItem="vuV-BV-bbx" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="tJb-jf-9mX"/>
|
||||
<constraint firstItem="0D1-Km-vTu" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="yJw-DU-ien"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="activityIndicatorView" destination="1j3-sd-BvY" id="NYR-SC-E82"/>
|
||||
<outlet property="attributionLabel" destination="jWW-0w-1YM" id="MtF-th-LJr"/>
|
||||
<outlet property="descriptionContainerView" destination="oVd-gS-Rmb" id="Npu-jp-oYo"/>
|
||||
<outlet property="descriptionIcon" destination="GP2-dA-giJ" id="7YL-UU-ClT"/>
|
||||
<outlet property="descriptionLabel" destination="c68-l7-McA" id="HiH-8Q-yTp"/>
|
||||
<outlet property="liveLocationContainerView" destination="Hpx-8P-nCb" id="Vxm-Sf-TAE"/>
|
||||
<outlet property="liveLocationIcon" destination="pgy-lK-ehN" id="1Th-PJ-G0p"/>
|
||||
<outlet property="liveLocationIconBackgroundView" destination="72o-VM-Ls7" id="2XU-x0-vhz"/>
|
||||
<outlet property="liveLocationStatusLabel" destination="X9R-EV-RDT" id="HJr-cE-icv"/>
|
||||
<outlet property="liveLocationTimerLabel" destination="U75-c5-got" id="RkO-6a-ABU"/>
|
||||
<outlet property="mapLoadingErrorContainerView" destination="XkV-iZ-sfD" id="kAm-JY-kRA"/>
|
||||
<outlet property="mapLoadingErrorImageView" destination="ror-cm-dP7" id="oXd-fx-43J"/>
|
||||
<outlet property="mapLoadingErrorMessageLabel" destination="Eml-nq-quj" id="Yix-87-5R6"/>
|
||||
<outlet property="placeholderBackground" destination="vuV-BV-bbx" id="9PW-RW-tFh"/>
|
||||
<outlet property="placeholderIconView" destination="4WL-gT-HBV" id="pqw-gN-2Q7"/>
|
||||
<outlet property="rightButton" destination="k5H-UE-ygH" id="nz0-Eb-SRp"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="165.94202898550725" y="-101.11607142857143"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="location_background_light_image" width="847.5" height="556"/>
|
||||
<image name="location_live_cell_ended_light_icon" width="46" height="65"/>
|
||||
<image name="location_live_cell_icon" width="32" height="32"/>
|
||||
<image name="location_map_error" width="33" height="32"/>
|
||||
<image name="location_marker_icon" width="24" height="24"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
||||
Reference in New Issue
Block a user