mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-03 22:56:57 +02:00
Merge branch 'aggregations' into riot_2393
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
Copyright 2019 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
|
||||
|
||||
final class ContextualMenuItemView: UIView, NibOwnerLoadable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum ColorAlpha {
|
||||
static let normal: CGFloat = 1.0
|
||||
static let highlighted: CGFloat = 0.3
|
||||
}
|
||||
|
||||
private enum ViewAlpha {
|
||||
static let normal: CGFloat = 1.0
|
||||
static let disabled: CGFloat = 0.5
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var imageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var originalImage: UIImage?
|
||||
|
||||
private var isHighlighted: Bool = false {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var titleColor: UIColor = .black {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var imageColor: UIColor = .black {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var isEnabled: Bool = true {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.setupGestureRecognizer()
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(title: String, image: UIImage?) {
|
||||
self.originalImage = image?.withRenderingMode(.alwaysTemplate)
|
||||
self.titleLabel.text = title
|
||||
self.updateView()
|
||||
}
|
||||
|
||||
func fill(menuItem: RoomContextualMenuItem) {
|
||||
self.fill(title: menuItem.title, image: menuItem.image)
|
||||
self.action = menuItem.action
|
||||
self.isEnabled = menuItem.isEnabled
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupGestureRecognizer() {
|
||||
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
|
||||
gestureRecognizer.minimumPressDuration = 0
|
||||
self.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
|
||||
let viewAlpha = self.isEnabled ? ViewAlpha.normal : ViewAlpha.disabled
|
||||
let colorAlpha = self.isHighlighted ? ColorAlpha.highlighted : ColorAlpha.normal
|
||||
|
||||
self.updateTitleAndImageAlpha(viewAlpha)
|
||||
self.imageView.tintColor = self.imageColor
|
||||
self.updateTitleAndImageColorAlpha(colorAlpha)
|
||||
}
|
||||
|
||||
private func updateTitleAndImageAlpha(_ alpha: CGFloat) {
|
||||
self.imageView.alpha = alpha
|
||||
self.titleLabel.alpha = alpha
|
||||
}
|
||||
|
||||
private func updateTitleAndImageColorAlpha(_ alpha: CGFloat) {
|
||||
let titleColor: UIColor
|
||||
let image: UIImage?
|
||||
|
||||
if alpha < 1.0 {
|
||||
titleColor = self.titleColor.withAlphaComponent(alpha)
|
||||
image = self.originalImage?.vc_tintedImage(usingColor: self.imageColor.withAlphaComponent(alpha))
|
||||
} else {
|
||||
titleColor = self.titleColor
|
||||
image = self.originalImage
|
||||
}
|
||||
|
||||
self.titleLabel.textColor = titleColor
|
||||
self.imageView.image = image
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func buttonAction(_ sender: UILongPressGestureRecognizer) {
|
||||
guard self.isEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
let isBackgroundViewTouched = sender.vc_isTouchingInside()
|
||||
|
||||
switch sender.state {
|
||||
case .began, .changed:
|
||||
self.isHighlighted = isBackgroundViewTouched
|
||||
case .ended:
|
||||
self.isHighlighted = false
|
||||
|
||||
if isBackgroundViewTouched {
|
||||
self.action?()
|
||||
}
|
||||
case .cancelled:
|
||||
self.isHighlighted = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ContextualMenuItemView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="imageView" destination="RcD-qR-Hvt" id="NnI-K5-aTj"/>
|
||||
<outlet property="titleLabel" destination="Wap-UK-AxI" id="AlN-i0-4IN"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Eav-pf-73a">
|
||||
<rect key="frame" x="0.0" y="0.0" width="64" height="69"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-copy" translatesAutoresizingMaskIntoConstraints="NO" id="RcD-qR-Hvt">
|
||||
<rect key="frame" x="21.5" y="8" width="21" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="RcD-qR-Hvt" secondAttribute="height" multiplier="21:29" id="V50-97-yzO"/>
|
||||
<constraint firstAttribute="height" constant="29" id="tBN-Ex-KO4"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Copy" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wap-UK-AxI">
|
||||
<rect key="frame" x="5" y="39" width="54" height="25"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="RcD-qR-Hvt" firstAttribute="centerX" secondItem="Eav-pf-73a" secondAttribute="centerX" id="6sB-0G-LfN"/>
|
||||
<constraint firstItem="Wap-UK-AxI" firstAttribute="leading" secondItem="Eav-pf-73a" secondAttribute="leading" constant="5" id="989-CC-w7E"/>
|
||||
<constraint firstItem="RcD-qR-Hvt" firstAttribute="top" secondItem="Eav-pf-73a" secondAttribute="top" constant="8" id="hos-Pl-Og0"/>
|
||||
<constraint firstItem="Wap-UK-AxI" firstAttribute="top" secondItem="RcD-qR-Hvt" secondAttribute="bottom" constant="2" id="iQ8-gd-onK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Wap-UK-AxI" secondAttribute="trailing" constant="5" id="tjm-F0-G8m"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Wap-UK-AxI" secondAttribute="bottom" constant="5" id="y2C-S8-hdF"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="-121.73913043478262" y="-31.138392857142854"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="icon-copy" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2019 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 Foundation
|
||||
|
||||
@objc enum RoomContextualMenuAction: Int {
|
||||
case copy
|
||||
case reply
|
||||
case edit
|
||||
case more
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var title: String {
|
||||
let title: String
|
||||
|
||||
switch self {
|
||||
case .copy:
|
||||
title = VectorL10n.roomEventActionCopy
|
||||
case .reply:
|
||||
title = VectorL10n.roomEventActionReply
|
||||
case .edit:
|
||||
title = VectorL10n.roomEventActionEdit
|
||||
case .more:
|
||||
title = VectorL10n.roomEventActionMore
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
var image: UIImage? {
|
||||
let image: UIImage?
|
||||
|
||||
switch self {
|
||||
case .copy:
|
||||
image = Asset.Images.roomContextMenuCopy.image
|
||||
case .reply:
|
||||
image = Asset.Images.roomContextMenuReply.image
|
||||
case .edit:
|
||||
image = Asset.Images.roomContextMenuEdit.image
|
||||
case .more:
|
||||
image = Asset.Images.roomContextMenuMore.image
|
||||
default:
|
||||
image = nil
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Copyright 2019 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 Foundation
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuItem: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
|
||||
var isEnabled: Bool = true
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(menuAction: RoomContextualMenuAction) {
|
||||
self.title = menuAction.title
|
||||
self.image = menuAction.image
|
||||
super.init()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright 2019 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 Foundation
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let animationDuration: TimeInterval = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var roomContextualMenuViewController: RoomContextualMenuViewController?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var isPresenting: Bool {
|
||||
return self.roomContextualMenuViewController != nil
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(roomContextualMenuViewController: RoomContextualMenuViewController,
|
||||
from viewController: UIViewController,
|
||||
on view: UIView,
|
||||
animated: Bool,
|
||||
completion: (() -> Void)?) {
|
||||
guard self.roomContextualMenuViewController == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
roomContextualMenuViewController.view.alpha = 0
|
||||
|
||||
viewController.vc_addChildViewController(viewController: roomContextualMenuViewController, onView: view)
|
||||
|
||||
self.roomContextualMenuViewController = roomContextualMenuViewController
|
||||
|
||||
roomContextualMenuViewController.hideMenuToolbar()
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
|
||||
let animationInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.showMenuToolbar()
|
||||
roomContextualMenuViewController.view.alpha = 1
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
if animated {
|
||||
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||
animationInstructions()
|
||||
}, completion: { completed in
|
||||
completion?()
|
||||
})
|
||||
} else {
|
||||
animationInstructions()
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
func hideContextualMenu(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let roomContextualMenuViewController = self.roomContextualMenuViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let animationInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.hideMenuToolbar()
|
||||
roomContextualMenuViewController.view.alpha = 0
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
let animationCompletionInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.vc_removeFromParent()
|
||||
completion?()
|
||||
}
|
||||
|
||||
if animated {
|
||||
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||
animationInstructions()
|
||||
}, completion: { completed in
|
||||
animationCompletionInstructions()
|
||||
})
|
||||
} else {
|
||||
animationInstructions()
|
||||
animationCompletionInstructions()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright 2019 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
|
||||
|
||||
final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoadable, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let menuItemMinWidth: CGFloat = 50.0
|
||||
static let menuItemMaxWidth: CGFloat = 80.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var menuItemsStackView: UIStackView!
|
||||
@IBOutlet private weak var separatorView: UIView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
private var menuItemViews: [ContextualMenuItemView] = []
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.tintColor = theme.tintColor
|
||||
self.separatorView.backgroundColor = theme.lineBreakColor
|
||||
|
||||
for menuItemView in self.menuItemViews {
|
||||
menuItemView.titleColor = theme.textPrimaryColor
|
||||
menuItemView.imageColor = theme.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
@objc func fill(contextualMenuItems: [RoomContextualMenuItem]) {
|
||||
self.menuItemsStackView.vc_removeAllSubviews()
|
||||
self.menuItemViews.removeAll()
|
||||
|
||||
for menuItem in contextualMenuItems {
|
||||
let menuItemView = ContextualMenuItemView()
|
||||
menuItemView.fill(menuItem: menuItem)
|
||||
|
||||
if let theme = theme {
|
||||
menuItemView.titleColor = theme.textPrimaryColor
|
||||
menuItemView.imageColor = theme.tintColor
|
||||
}
|
||||
|
||||
self.add(menuItemView: menuItemView)
|
||||
}
|
||||
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func commonInit() {
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func add(menuItemView: ContextualMenuItemView) {
|
||||
let menuItemContentView = UIView()
|
||||
menuItemContentView.backgroundColor = .clear
|
||||
|
||||
self.add(menuItemView: menuItemView, on: menuItemContentView)
|
||||
|
||||
self.menuItemsStackView.addArrangedSubview(menuItemContentView)
|
||||
|
||||
let widthConstraint = menuItemContentView.widthAnchor.constraint(equalTo: self.menuItemsStackView.widthAnchor)
|
||||
widthConstraint.priority = .defaultLow
|
||||
widthConstraint.isActive = true
|
||||
|
||||
self.menuItemViews.append(menuItemView)
|
||||
}
|
||||
|
||||
private func add(menuItemView: ContextualMenuItemView, on contentView: UIView) {
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
menuItemView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
contentView.addSubview(menuItemView)
|
||||
|
||||
menuItemView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
|
||||
menuItemView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
|
||||
|
||||
let widthConstraint = menuItemView.widthAnchor.constraint(equalToConstant: 0.0)
|
||||
widthConstraint.priority = .defaultLow
|
||||
widthConstraint.isActive = true
|
||||
|
||||
let minWidthConstraint = menuItemView.widthAnchor.constraint(greaterThanOrEqualToConstant: Constants.menuItemMinWidth)
|
||||
minWidthConstraint.priority = .required
|
||||
minWidthConstraint.isActive = true
|
||||
|
||||
let maxWidthConstraint = menuItemView.widthAnchor.constraint(lessThanOrEqualToConstant: Constants.menuItemMaxWidth)
|
||||
maxWidthConstraint.priority = .required
|
||||
maxWidthConstraint.isActive = true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RoomContextualMenuToolbarView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="menuItemsStackView" destination="ayT-FO-8xC" id="v7N-rd-lEb"/>
|
||||
<outlet property="separatorView" destination="dzn-dX-0at" id="KVI-n6-pkG"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="gik-f6-38I">
|
||||
<rect key="frame" x="0.0" y="0.0" width="470" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dzn-dX-0at" userLabel="Separator View">
|
||||
<rect key="frame" x="10" y="0.0" width="450" height="1"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="Ktg-wG-Xuk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="ayT-FO-8xC">
|
||||
<rect key="frame" x="0.0" y="1" width="470" height="62"/>
|
||||
</stackView>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kgj-Hq-tEB">
|
||||
<rect key="frame" x="0.0" y="1" width="414" height="52"/>
|
||||
<items>
|
||||
<barButtonItem title="Copy" id="P1Q-AZ-Qem" userLabel="Copy"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="bzo-GF-Clo"/>
|
||||
<barButtonItem title="Reply" id="pW0-ss-bsI"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="bbf-N0-Zis"/>
|
||||
<barButtonItem title="Edit" id="X3Q-SB-6e1"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="2D4-jO-fyW"/>
|
||||
<barButtonItem title="More" id="skA-D0-EBc"/>
|
||||
</items>
|
||||
</toolbar>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dzn-dX-0at" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" constant="10" id="0qV-0P-IyR"/>
|
||||
<constraint firstItem="kgj-Hq-tEB" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" id="14D-JP-KDd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ayT-FO-8xC" secondAttribute="bottom" id="CtJ-Y4-5vr"/>
|
||||
<constraint firstAttribute="bottom" secondItem="kgj-Hq-tEB" secondAttribute="bottom" constant="1" id="RpB-Ka-WmR"/>
|
||||
<constraint firstItem="kgj-Hq-tEB" firstAttribute="top" secondItem="dzn-dX-0at" secondAttribute="bottom" id="SSI-IW-4eG"/>
|
||||
<constraint firstItem="ayT-FO-8xC" firstAttribute="top" secondItem="dzn-dX-0at" secondAttribute="bottom" id="Stq-dM-Psw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ayT-FO-8xC" secondAttribute="trailing" id="cPG-PI-jhM"/>
|
||||
<constraint firstItem="dzn-dX-0at" firstAttribute="top" secondItem="gik-f6-38I" secondAttribute="top" id="dFk-HG-k1l"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dzn-dX-0at" secondAttribute="trailing" constant="10" id="uxF-0x-HFI"/>
|
||||
<constraint firstItem="ayT-FO-8xC" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" id="yDp-d9-du7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kgj-Hq-tEB" secondAttribute="trailing" id="yrf-JI-gvp"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<variation key="default">
|
||||
<mask key="subviews">
|
||||
<exclude reference="kgj-Hq-tEB"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<point key="canvasLocation" x="-1457.9710144927537" y="-643.19196428571422"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="2eW-Ga-w3t">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Room Contextual Menu View Controller-->
|
||||
<scene sceneID="I8V-hb-Jea">
|
||||
<objects>
|
||||
<viewController id="2eW-Ga-w3t" customClass="RoomContextualMenuViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="X0o-r8-auN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Szx-Dr-Ndt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="793"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0GC-JU-rI3" customClass="RoomContextualMenuToolbarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="793" width="414" height="69"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="69" id="ynL-KP-iB4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Szx-Dr-Ndt" secondAttribute="trailing" id="2eB-6O-P3h"/>
|
||||
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="4qK-G6-nr9"/>
|
||||
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="top" secondItem="X0o-r8-auN" secondAttribute="top" id="GVa-P9-DcG"/>
|
||||
<constraint firstItem="0GC-JU-rI3" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="TZJ-nm-Ppz"/>
|
||||
<constraint firstItem="0GC-JU-rI3" firstAttribute="top" secondItem="Szx-Dr-Ndt" secondAttribute="bottom" id="Wyl-wh-kh4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0GC-JU-rI3" secondAttribute="trailing" id="lzM-FD-x89"/>
|
||||
<constraint firstItem="225-y0-Elg" firstAttribute="bottom" secondItem="0GC-JU-rI3" secondAttribute="bottom" id="s4i-80-0iu"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="225-y0-Elg"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="backgroundOverlayView" destination="Szx-Dr-Ndt" id="Whj-e5-bas"/>
|
||||
<outlet property="menuToolbarView" destination="0GC-JU-rI3" id="j0z-I8-Pcr"/>
|
||||
<outlet property="menuToolbarViewBottomConstraint" destination="s4i-80-0iu" id="E5w-5m-m5O"/>
|
||||
<outlet property="menuToolbarViewHeightConstraint" destination="ynL-KP-iB4" id="Zeb-b0-Yil"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8NV-wl-Hp0" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53.623188405797109" y="135.9375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Copyright 2019 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
|
||||
|
||||
@objc protocol RoomContextualMenuViewControllerDelegate: class {
|
||||
func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuViewController: UIViewController, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var backgroundOverlayView: UIView!
|
||||
@IBOutlet private weak var menuToolbarView: RoomContextualMenuToolbarView!
|
||||
|
||||
@IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint!
|
||||
@IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var contextualMenuItems: [RoomContextualMenuItem] = []
|
||||
|
||||
private var hiddenToolbarViewBottomConstant: CGFloat {
|
||||
let bottomSafeAreaHeight: CGFloat
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeAreaHeight = self.view.safeAreaInsets.bottom
|
||||
} else {
|
||||
bottomSafeAreaHeight = self.bottomLayoutGuide.length
|
||||
}
|
||||
|
||||
return -(self.menuToolbarViewHeightConstraint.constant + bottomSafeAreaHeight)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: RoomContextualMenuViewControllerDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with contextualMenuItems: [RoomContextualMenuItem]) -> RoomContextualMenuViewController {
|
||||
let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate()
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
viewController.contextualMenuItems = contextualMenuItems
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.backgroundOverlayView.isUserInteractionEnabled = true
|
||||
self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems)
|
||||
self.setupBackgroundOverlayTapGestureRecognizer()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func showMenuToolbar() {
|
||||
self.menuToolbarViewBottomConstraint.constant = 0
|
||||
}
|
||||
|
||||
func hideMenuToolbar() {
|
||||
self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.menuToolbarView.update(theme: theme)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupBackgroundOverlayTapGestureRecognizer() {
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:)))
|
||||
self.backgroundOverlayView.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc private func handleTap(gestureRecognizer: UIGestureRecognizer) {
|
||||
self.delegate?.roomContextualMenuViewControllerDidTapBackgroundOverlay(self)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user