- tool box menu improvements

- autoDock improvements
- live gamepad overlay close button
This commit is contained in:
True Zhuanjia
2026-04-16 14:31:21 +08:00
parent d769c98e22
commit a1a70fcca2
8 changed files with 214 additions and 29 deletions
@@ -9,6 +9,12 @@
import UIKit
import SwiftUI
@objc
protocol AbstractGamepadOverlayCloseButtonDelegate: AnyObject {
@objc(toggleGamepadOverlayWithOverlayEnabled:)
func toggleGamepadOverlay(withOverlayEnabled overlayEnabled: Bool)
}
private enum AbstractGamepadOverlayPersistence {
private static let defaults = UserDefaults.standard
@@ -126,6 +132,14 @@ private struct AbstractGamepadOverlayBridgeView: View {
@available(iOS 13.0, *)
@objc
final class AbstractGamepadOverlayView: UIView {
private enum CloseButtonMetrics {
static let size: CGFloat = 40
static let topInset: CGFloat = -10
static let trailingInset: CGFloat = 0
static let symbolPointSize: CGFloat = 16
static let hideDelay: TimeInterval = 3
}
private enum OverlayMaskMetrics {
static let widthRatio: CGFloat = 0.85
static let heightRatio: CGFloat = 1.03
@@ -149,9 +163,12 @@ final class AbstractGamepadOverlayView: UIView {
private var panStartCenter: CGPoint = .zero
private var activeGestureCount = 0
private var didRestorePersistedState = false
private var hideCloseButtonWorkItem: DispatchWorkItem?
private let minWidth: CGFloat = 70
private let maxWidth: CGFloat = 520
private let doubleTapWidths: [CGFloat] = [GenericUtils.isIPhone() ? 90 : 130, GenericUtils.isIPhone() ? 165 : 200]
private lazy var closeButton: UIButton = makeCloseButton()
@objc weak var closeButtonDelegate: AbstractGamepadOverlayCloseButtonDelegate?
@objc init(frame: CGRect, usesPlayStationFaceButtons: Bool) {
self.usesPlayStationFaceButtons = usesPlayStationFaceButtons
@@ -189,16 +206,17 @@ final class AbstractGamepadOverlayView: UIView {
backgroundColor = .clear
clipsToBounds = false
isMultipleTouchEnabled = true
layer.mask = visualMaskLayer
applyScale(forTargetWidth: initialDisplayWidth)
addSubview(hostingController.view)
addSubview(closeButton)
installGesturesIfNeeded()
}
override func layoutSubviews() {
super.layoutSubviews()
hostedView?.frame = bounds
layoutCloseButton()
updateVisualMask()
restorePersistedStateIfNeeded()
}
@@ -237,17 +255,21 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
guard recognizer.state == .ended else { return }
registerUserInteraction()
presentFirstTouchTipIfNeeded()
scheduleCloseButtonHideIfNeeded()
}
@objc private func handlePan(_ recognizer: UIPanGestureRecognizer) {
guard let superview else { return }
switch recognizer.state {
case .began:
registerUserInteraction()
presentFirstTouchTipIfNeeded()
beginGestureSession()
panStartCenter = center
case .changed, .ended:
registerUserInteraction()
let translation = recognizer.translation(in: superview)
center = CGPoint(x: panStartCenter.x + translation.x, y: panStartCenter.y + translation.y)
if recognizer.state == .ended {
@@ -263,10 +285,12 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handlePinch(_ recognizer: UIPinchGestureRecognizer) {
switch recognizer.state {
case .began:
registerUserInteraction()
presentFirstTouchTipIfNeeded()
beginGestureSession()
pinchBaseWidth = frame.width
case .changed, .ended:
registerUserInteraction()
let nextWidth = max(minWidth, min(maxWidth, pinchBaseWidth * recognizer.scale))
applyScale(forTargetWidth: nextWidth)
if recognizer.state == .ended {
@@ -281,14 +305,17 @@ final class AbstractGamepadOverlayView: UIView {
@objc private func handleDoubleTap(_ recognizer: UITapGestureRecognizer) {
guard recognizer.state == .ended else { return }
registerUserInteraction()
presentFirstTouchTipIfNeeded()
applyScale(forTargetWidth: nextDoubleTapWidth())
persistState()
scheduleCloseButtonHideIfNeeded()
}
private func updateVisualMask() {
guard bounds.width > 1, bounds.height > 1 else {
visualMaskLayer.path = nil
hostedView?.layer.mask = nil
return
}
@@ -325,16 +352,32 @@ final class AbstractGamepadOverlayView: UIView {
)
)
visualMaskLayer.path = path.cgPath
hostedView?.layer.mask = visualMaskLayer
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
bounds.contains(point)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
registerUserInteraction()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesMoved(touches, with: event)
registerUserInteraction()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
scheduleCloseButtonHideIfNeeded()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
scheduleCloseButtonHideIfNeeded()
}
private func restorePersistedStateIfNeeded() {
guard !didRestorePersistedState,
@@ -354,6 +397,11 @@ final class AbstractGamepadOverlayView: UIView {
activeGestureCount += 1
}
@objc func registerUserInteraction() {
showCloseButton()
cancelPendingCloseButtonHide()
}
private func presentFirstTouchTipIfNeeded() {
guard GenericUtils.isFirstLaunchGamepadOverlayFeature() else { return }
guard let presentingViewController = nearestViewController() else { return }
@@ -383,6 +431,7 @@ final class AbstractGamepadOverlayView: UIView {
activeGestureCount = max(0, activeGestureCount - 1)
guard activeGestureCount == 0 else { return }
persistState()
scheduleCloseButtonHideIfNeeded()
}
private func persistState() {
@@ -407,6 +456,79 @@ final class AbstractGamepadOverlayView: UIView {
let targetScale = clampedWidth / baseRenderWidth
transform = CGAffineTransform(scaleX: targetScale, y: targetScale)
}
private func makeCloseButton() -> UIButton {
let button = UIButton(type: .system)
button.tintColor = UIColor.black.withAlphaComponent(0.66)
button.backgroundColor = UIColor.white.withAlphaComponent(0.78)
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.white.withAlphaComponent(0.9).cgColor
button.layer.shadowColor = UIColor.black.withAlphaComponent(0.05).cgColor
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 4
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.alpha = 0
button.isHidden = true
button.adjustsImageWhenHighlighted = false
button.addTarget(self, action: #selector(handleCloseButtonTap), for: .touchUpInside)
let configuration = UIImage.SymbolConfiguration(pointSize: CloseButtonMetrics.symbolPointSize, weight: .bold)
button.setImage(UIImage(systemName: "xmark", withConfiguration: configuration), for: .normal)
return button
}
private func layoutCloseButton() {
let size = CGFloat(Int(CloseButtonMetrics.size/2))*2
closeButton.frame = CGRect(
x: bounds.width - CloseButtonMetrics.trailingInset - size,
y: CloseButtonMetrics.topInset,
width: size,
height: size
)
closeButton.layer.cornerRadius = size * 0.5
}
private func showCloseButton() {
closeButton.isHidden = false
guard closeButton.alpha < 1 else { return }
UIView.animate(withDuration: 0.18) {
self.closeButton.alpha = 1
}
}
private func hideCloseButton() {
guard !closeButton.isHidden else { return }
UIView.animate(
withDuration: 0.18,
animations: {
self.closeButton.alpha = 0
},
completion: { _ in
self.closeButton.isHidden = true
}
)
}
private func cancelPendingCloseButtonHide() {
hideCloseButtonWorkItem?.cancel()
hideCloseButtonWorkItem = nil
}
@objc func scheduleCloseButtonHideIfNeeded() {
guard activeGestureCount == 0 else { return }
cancelPendingCloseButtonHide()
let workItem = DispatchWorkItem { [weak self] in
self?.hideCloseButton()
self?.hideCloseButtonWorkItem = nil
}
hideCloseButtonWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + CloseButtonMetrics.hideDelay, execute: workItem)
}
@objc private func handleCloseButtonTap() {
cancelPendingCloseButtonHide()
closeButtonDelegate?.toggleGamepadOverlay(withOverlayEnabled: false)
}
}
@available(iOS 13.0, *)
@@ -415,4 +537,9 @@ extension AbstractGamepadOverlayView: UIGestureRecognizerDelegate {
(gestureRecognizer is UIPanGestureRecognizer && otherGestureRecognizer is UIPinchGestureRecognizer)
|| (gestureRecognizer is UIPinchGestureRecognizer && otherGestureRecognizer is UIPanGestureRecognizer)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
guard let touchedView = touch.view else { return true }
return touchedView !== closeButton && !touchedView.isDescendant(of: closeButton)
}
}
+1 -1
View File
@@ -114,7 +114,7 @@
self.reverseGyroHoldButton = [decoder containsValueForKey:@"reverseGyroHoldButton"] ? [decoder decodeBoolForKey:@"reverseGyroHoldButton"] : false;
self.controllerGyroSwitchHold = [decoder containsValueForKey:@"controllerGyroSwitchHold"] ? [decoder decodeIntForKey:@"controllerGyroSwitchHold"] : ControllerButtonNull;
self.controllerGyroSwitchToggle = [decoder containsValueForKey:@"controllerGyroSwitchToggle"] ? [decoder decodeIntForKey:@"controllerGyroSwitchToggle"] : ControllerButtonNull;
self.gamepadOverlayEnabled = [decoder containsValueForKey:@"gamepadOverlayEnabled"] ? [decoder decodeBoolForKey:@"gamepadOverlayEnabled"] : true;
self.gamepadOverlayEnabled = [decoder containsValueForKey:@"gamepadOverlayEnabled"] ? [decoder decodeBoolForKey:@"gamepadOverlayEnabled"] : false;
self.pressureCurvePoints =
[decoder containsValueForKey:@"pressureCurvePoints"]
+17 -9
View File
@@ -143,6 +143,7 @@ import ObjectiveC.runtime
// for movable buttons during streaming
@objc public var relocatedDuringStreaming: Bool = false
@objc static var relocatedDuringStreaming: Bool = false
// for all touchPad or buttons hybrid with touchPads
@objc public var hasMinStickOffset: Bool = false
@@ -847,7 +848,7 @@ import ObjectiveC.runtime
case "DISABLETOUCH":
self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: touchDisabledFLag ? "=EnableTouch" : "=DisableTouch" )
case "GAMEPADOVERLAY":
self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: gamepadOverlayFLag ? "=GamepadOverlayOn" : "=GamepadOverlayOff" )
self.nonEditableWidgetLabel = SwiftLocalizationHelper.localizedString(forKey: OnScreenWidgetView.gamepadOverlayFLag ? "=GamepadOverlayOn" : "=GamepadOverlayOff" )
default:
nonEditableWidgetLabel = ""
}
@@ -2715,13 +2716,13 @@ import ObjectiveC.runtime
self.functionalButtonDelegate?.toggleTouch(disabled: touchDisabledFLag)
}
@objc var gamepadOverlayFLag:Bool = false
@objc static var gamepadOverlayFLag:Bool = false
private func gamepadOverlayButtonUp(){
self.relocatedDuringStreaming = true
gamepadOverlayFLag = !gamepadOverlayFLag
OnScreenWidgetView.gamepadOverlayFLag = !OnScreenWidgetView.gamepadOverlayFLag
self.setupAtrributedText()
if #available(iOS 13.0, *) {
self.functionalButtonDelegate?.toggleGamepadOverlay(overlayEnabled: gamepadOverlayFLag)
self.functionalButtonDelegate?.toggleGamepadOverlay(overlayEnabled: OnScreenWidgetView.gamepadOverlayFLag)
}
}
@@ -3093,6 +3094,7 @@ import ObjectiveC.runtime
private static let autoDockExposedThickness: CGFloat = 22
private static let autoDockVerticalInset: CGFloat = 12
@objc var autoDockIdleDuration: TimeInterval = 0
@objc var storedAutoDockIdleDuration: TimeInterval = 0
private static let autoDockInitialAlpha: CGFloat = 0.8
@objc var autoDockSettledAlpha: CGFloat = 0.2
private static let autoDockSettledAlphaDelay: TimeInterval = 2
@@ -3123,8 +3125,8 @@ import ObjectiveC.runtime
private var autoDockDockedCenter: CGPoint?
private var autoDockDockedToBottomEdge: Bool = false
private var autoDockDistance: CGFloat = 3
private var autoDockIsDocked: Bool = false
private var autoDockEnabled: Bool = false
@objc var autoDockIsDocked: Bool = false
@objc var autoDockEnabled: Bool = false
private var autoDockSettledAlphaTimer: Timer?
private var autoDockOriginalBoundsSize: CGSize = .zero
@@ -3174,7 +3176,7 @@ import ObjectiveC.runtime
autoDockRestartCountdownIfNeeded()
}
@objc public func setAutoDockEnabled(_ enabled: Bool) {
@objc public func setAutoDock(enabled: Bool) {
autoDockEnabled = enabled
if enabled {
OnScreenWidgetView.installAutoDockIfNeeded()
@@ -3212,7 +3214,7 @@ import ObjectiveC.runtime
autoDockRestoreWidget(animated: animated)
}
private func autoDockStopCountdown() {
@objc func autoDockStopCountdown() {
autoDockTimer?.invalidate()
autoDockTimer = nil
}
@@ -3285,6 +3287,8 @@ import ObjectiveC.runtime
private func autoDockWidgetToNearestEdge() {
guard autoDockEnabled else {return}
autoDockIdleDuration = storedAutoDockIdleDuration
if !folded {
restartAutoDockCountdown()
return
@@ -3342,7 +3346,10 @@ import ObjectiveC.runtime
}
private func autoDockRestoreWidget(animated: Bool) {
guard autoDockIsDocked else { return }
guard autoDockIsDocked else {
restartAutoDockCountdown()
return
}
let restoreCenter = storedCenter
OnScreenWidgetView.hasRestoredFromAutoDock = true
autoDockStopCountdown()
@@ -3475,6 +3482,7 @@ import ObjectiveC.runtime
|| abs(widget.backgroundAlpha) < 0.1){
widget.highlightBorder(highlighted: false)
}
if widget.hasNonEditableLabel {widget.setupAtrributedText()}
})
}
}
+6 -4
View File
@@ -603,7 +603,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
if(!hasFolderStateChanged) hasFolderStateChanged = widget.folded != widget.persistedFolded;
}
if(relocatedWidgetSequences.count == 0 && !hasFolderStateChanged) return;
if(relocatedWidgetSequences.count == 0 && !hasFolderStateChanged && !OnScreenWidgetView.relocatedDuringStreaming) return;
NSLog(@"relocatedWidgetSequences.count %lu, hasFolderStateChanged %d %F", relocatedWidgetSequences.count, hasFolderStateChanged, CACurrentMediaTime());
oscProfileMan = [OSCProfilesManager sharedManager:self->_streamFrameTopLayerView.bounds];
@@ -651,7 +651,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
if(![self isOnScreenWidgetEnabled]) return;
OnScreenWidgetView.buttonVisualFeedbackEnabled = self->settings.buttonVisualFeedback;
OnScreenWidgetView.gamepadOverlayFLag = oscProfile.gamepadOverlayEnabled;
OnScreenWidgetView.relocatedDuringStreaming = false;
bool hasLegacyWidget = false;
if(reload && !OnScreenWidgetView.editMode){
// remove all keyboard widget views first
@@ -675,6 +677,7 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
widgetView.sequenceSet = buttonState.sequenceSet;
widgetView.parentSequence = buttonState.parentSequence;
widgetView.autoDockIdleDuration = buttonState.autoDockTimer;
widgetView.storedAutoDockIdleDuration = widgetView.autoDockIdleDuration;
widgetView.autoDockSettledAlpha = buttonState.dockedAlpha;
widgetView.folded = buttonState.folded;
widgetView.persistedFolded = buttonState.folded;
@@ -723,10 +726,9 @@ static const double X1_MOUSE_SPEED_DIVISOR = 2.5;
[widgetView setupAutoTapTimer];
[widgetView setupInertialScrollerWithFps:self->settings.framerate.intValue];
widgetView.gamepadOverlayFLag = oscProfile.gamepadOverlayEnabled;
[widgetView setupAtrributedText];
if(widgetView.isFolder && widgetView.parentSequence<0 && widgetView.autoDockIdleDuration>0) [widgetView setAutoDockEnabled:true];
if(widgetView.isFolder && widgetView.parentSequence<0 && widgetView.autoDockIdleDuration>0) [widgetView setAutoDockWithEnabled:true];
if(sequenceGenerated){
// NSLog(@"widgetView.sequence %d %f", widgetView.sequence, CACurrentMediaTime());
+12 -8
View File
@@ -213,16 +213,20 @@ import Foundation
return CGFloat(Double(str) ?? 0)
}
@available(iOS 13.0, *)
@objc static func isLandscape() -> Bool {
guard let windowScene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first(where: { $0.activationState == .foregroundActive })
else {
return false
if #available(iOS 13.0, *) {
guard let windowScene = UIApplication.shared.connectedScenes
.compactMap({ $0 as? UIWindowScene })
.first(where: { $0.activationState == .foregroundActive })
else {return false}
return windowScene.interfaceOrientation.isLandscape
}
return windowScene.interfaceOrientation.isLandscape
else {return GenericUtils.screenWidth > GenericUtils.screenHeight}
}
@objc static func viewIsLandscape(_ view: UIView?) -> Bool {
guard let view else {return false}
return view.bounds.width > view.bounds.height
}
@objc static var screenWidth: CGFloat {
@@ -28,7 +28,7 @@
@interface StreamFrameViewController : GCEventViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, AVPictureInPictureControllerDelegate>
#else
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate, OnScreenFunctionalButtonDelegate>
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, ControllerSupportDelegate, UserInteractionDelegate, UIScrollViewDelegate, ToolboxSpecialEntryDelegate, AVPictureInPictureControllerDelegate, OnScreenFunctionalButtonDelegate, AbstractGamepadOverlayCloseButtonDelegate>
#endif
@property (nonatomic, strong) StreamManager* streamMan;
@@ -48,6 +48,7 @@
- (void)setUserInteractionEnabledForStreamView:(bool)enabled;
- (bool)shallDisableGyroHotSwitch;
- (void)loadGameProfileConfigs:(OSCProfile* )profile;
- (void)toggleGamepadOverlayWithOverlayEnabled:(BOOL)overlayEnabled API_AVAILABLE(ios(13.0));
- (void)loadAbstractGamepadOverlayIfNeeded API_AVAILABLE(ios(13.0));
@end
@@ -45,6 +45,7 @@
- (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2;
@end
@implementation StreamFrameViewController {
ControllerSupport *_controllerSupport;
TemporarySettings *_settings;
@@ -1750,6 +1751,16 @@
for(OnScreenWidgetView* widget in OnScreenWidgetView.mapping.allValues){
CGPoint oldCenter = widget.center;
CGPoint oldStoredCenter = widget.storedCenter;
if(widget.autoDockEnabled){
widget.autoDockIdleDuration = fmax(widget.autoDockIdleDuration, 2.0);
[widget autoDockStopCountdown];
}
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC));
dispatch_after(delayTime, dispatch_get_main_queue(), ^{
if(widget.autoDockEnabled) [widget restoreFromAutoDockWithAnimated:true];
});
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
widget.center = CGPointMake(oldCenter.x * scaleX,oldCenter.y * scaleY);
widget.storedCenter = CGPointMake(oldStoredCenter.x * scaleX,oldStoredCenter.y * scaleY);
@@ -1793,6 +1804,9 @@
}
- (void)toggleGamepadOverlayWithOverlayEnabled:(BOOL)overlayEnabled API_AVAILABLE(ios(13.0)){
OnScreenWidgetView.gamepadOverlayFLag = overlayEnabled;
OnScreenWidgetView.relocatedDuringStreaming = true;
if(overlayEnabled) [self loadAbstractGamepadOverlayIfNeeded];
else {
[_virtualGamepadOverlay removeFromSuperview];
@@ -1819,9 +1833,12 @@
CGRect overlayFrame = CGRectMake(0, 0, standardWidth, standardhHeight);
AbstractGamepadOverlayView *overlayView = [[AbstractGamepadOverlayView alloc] initWithFrame:overlayFrame usesPlayStationFaceButtons:!usesXboxFaceButtons];
overlayView.closeButtonDelegate = self;
overlayView.center = CGPointMake(self.view.bounds.size.width-standardWidth/2-20, self.view.bounds.size.height-standardhHeight/2-20);
overlayView.userInteractionEnabled = YES;
[self.view addSubview:overlayView];
[overlayView registerUserInteraction];
[overlayView scheduleCloseButtonHideIfNeeded];
self->_virtualGamepadOverlay = overlayView;
});
@@ -83,6 +83,19 @@ import UIKit
//setupConstraints()
}
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
DispatchQueue.main.asyncAfter(deadline: .now()+0.2) {
self.pinButton.isHidden = !GenericUtils.viewIsLandscape(self.view) && GenericUtils.isIPhone()
}
view.setNeedsUpdateConstraints()
coordinator.animate(alongsideTransition: { _ in
self.view.layoutIfNeeded()
})
}
private func setupViews() {
contentView = UIView(frame: self.view.frame)
@@ -129,6 +142,7 @@ import UIKit
contentView.addSubview(editButton)
// contentView.addSubview(exitButton)
contentView.addSubview(pinButton)
pinButton.isHidden = !GenericUtils.isLandscape()
self.view.addSubview(contentView)
@@ -149,6 +163,18 @@ import UIKit
private func isIPhone()->Bool {
return UIDevice.current.userInterfaceIdiom == .phone
}
private func contentWidthMultiplier() -> CGFloat {
/*
if isIPhone() && GenericUtils.viewIsLandscape(self.view) {
return 0.85
}*/
return isIPhone()
? (GenericUtils.viewIsLandscape(self.view) ? 0.6 : 0.85)
: (GenericUtils.viewIsLandscape(self.view) ? 0.6 : 0.85)
}
@objc public func setupConstraints() {
@@ -167,7 +193,7 @@ import UIKit
NSLayoutConstraint.activate([
// Set the width and height of the view
contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: isIPhone() ? 0.6 : 0.52),
contentView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: contentWidthMultiplier()),
contentView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.93),
// Set the width and height of the view
//view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 60),