Merge branch 'develop' into feature/healine_body_button_molecule
# Conflicts: # MVMCoreUI.xcodeproj/project.pbxproj # MVMCoreUI/OtherHandlers/MVMCoreUIMoleculeMappingObject.m
This commit is contained in:
commit
3178fa0223
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objectVersion = 48;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -19,6 +19,10 @@
|
||||
01E569D3223FFFA500327251 /* ThreeLayerViewController.swift in Headers */ = {isa = PBXBuildFile; fileRef = D2A5146A2214905000345BFB /* ThreeLayerViewController.swift */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0A1214A022C11A18007C7030 /* ActionDetailWithImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */; };
|
||||
0A7BAD74232A8DC700FB8E22 /* HeadlineBodyButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */; };
|
||||
0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */; };
|
||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */; };
|
||||
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */; };
|
||||
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */; };
|
||||
948DB67E2326DCD90011F916 /* MultiProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 948DB67D2326DCD90011F916 /* MultiProgress.swift */; };
|
||||
B8200E152280C4CF007245F4 /* ProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E142280C4CF007245F4 /* ProgressBar.swift */; };
|
||||
B8200E192281DC1A007245F4 /* CornerLabels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8200E182281DC1A007245F4 /* CornerLabels.swift */; };
|
||||
@ -43,6 +47,7 @@
|
||||
D260D7B622D68514007E7233 /* MVMCoreUIPagingProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D274CA332236A78900B01B62 /* StandardFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D274CA322236A78900B01B62 /* StandardFooterView.swift */; };
|
||||
D27CD40E2322EEAF00C1DC07 /* TabsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */; };
|
||||
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */; };
|
||||
D282AAB4223FDDAE00C46919 /* MFLoadImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */; };
|
||||
D282AABA224131D100C46919 /* MFTransparentGIFView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */; };
|
||||
D282AACB2243C61700C46919 /* ButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D282AACA2243C61700C46919 /* ButtonView.swift */; };
|
||||
@ -150,7 +155,6 @@
|
||||
D29DF2CB21E7BFCC003B2FB9 /* MFSizeThreshold.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14721E68728003B2FB9 /* MFSizeThreshold.m */; };
|
||||
D29DF2CE21E7C104003B2FB9 /* MFLoadingViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D29DF2CF21E7C104003B2FB9 /* MFLoadingViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */; };
|
||||
D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */; };
|
||||
D29DF2E121E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D29DF2EE21ECEADF003B2FB9 /* MFFonts.h in Headers */ = {isa = PBXBuildFile; fileRef = D29DF14D21E693AD003B2FB9 /* MFFonts.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */ = {isa = PBXBuildFile; fileRef = D29DF14C21E693AD003B2FB9 /* MFFonts.m */; };
|
||||
@ -205,6 +209,10 @@
|
||||
0A12149F22C11A17007C7030 /* ActionDetailWithImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionDetailWithImage.swift; sourceTree = "<group>"; };
|
||||
0A7BAD73232A8DC700FB8E22 /* HeadlineBodyButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyButton.swift; sourceTree = "<group>"; };
|
||||
0A804A3C2316CB79009A8656 /* HeadlineBodyPrimaryButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeadlineBodyPrimaryButton.swift; sourceTree = "<group>"; };
|
||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CATransaction+Extension.swift"; sourceTree = "<group>"; };
|
||||
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = "<group>"; };
|
||||
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxWithLabelView.swift; sourceTree = "<group>"; };
|
||||
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
|
||||
948DB67D2326DCD90011F916 /* MultiProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiProgress.swift; sourceTree = "<group>"; };
|
||||
B8200E142280C4CF007245F4 /* ProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressBar.swift; sourceTree = "<group>"; };
|
||||
B8200E182281DC1A007245F4 /* CornerLabels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CornerLabels.swift; sourceTree = "<group>"; };
|
||||
@ -229,6 +237,7 @@
|
||||
D260D7B522D68509007E7233 /* MVMCoreUIPagingProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPagingProtocol.h; sourceTree = "<group>"; };
|
||||
D274CA322236A78900B01B62 /* StandardFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StandardFooterView.swift; sourceTree = "<group>"; };
|
||||
D27CD40D2322EEAF00C1DC07 /* TabsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTableViewCell.swift; sourceTree = "<group>"; };
|
||||
D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EyebrowHeadlineBodyLink.swift; sourceTree = "<group>"; };
|
||||
D282AAB3223FDDAE00C46919 /* MFLoadImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFLoadImageView.swift; sourceTree = "<group>"; };
|
||||
D282AAB9224131D100C46919 /* MFTransparentGIFView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MFTransparentGIFView.swift; sourceTree = "<group>"; };
|
||||
D282AACA2243C61700C46919 /* ButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonView.swift; sourceTree = "<group>"; };
|
||||
@ -340,7 +349,6 @@
|
||||
D29DF2C321E7BF57003B2FB9 /* MFTabBarInteractor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFTabBarInteractor.m; sourceTree = "<group>"; };
|
||||
D29DF2CC21E7C104003B2FB9 /* MFLoadingViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MFLoadingViewController.h; sourceTree = "<group>"; };
|
||||
D29DF2CD21E7C104003B2FB9 /* MFLoadingViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MFLoadingViewController.m; sourceTree = "<group>"; };
|
||||
D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MVMAnimationFramework.framework; path = ../SharedFrameworks/MVMAnimationFramework.framework; sourceTree = "<group>"; };
|
||||
D29DF2E021E9240B003B2FB9 /* MVMCoreUIPanelProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVMCoreUIPanelProtocol.h; sourceTree = "<group>"; };
|
||||
D29DF31621ECECC0003B2FB9 /* NHaasGroteskDSStd-45Lt.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "NHaasGroteskDSStd-45Lt.otf"; sourceTree = "<group>"; };
|
||||
D29DF31721ECECC0003B2FB9 /* OCRAExtended.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = OCRAExtended.ttf; sourceTree = "<group>"; };
|
||||
@ -388,7 +396,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D29DF0E621E4F3C7003B2FB9 /* MVMCore.framework in Frameworks */,
|
||||
D29DF2D121E7C1C8003B2FB9 /* MVMAnimationFramework.framework in Frameworks */,
|
||||
9455B19C234F8A0400A574DB /* MVMAnimationFramework.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -421,6 +429,7 @@
|
||||
children = (
|
||||
D2A638FC22CA98280052ED1F /* HeadlineBody.swift */,
|
||||
D22479952316AF6D003FCCF9 /* HeadlineBodyTextButton.swift */,
|
||||
D27CD40F2339057800C1DC07 /* EyebrowHeadlineBodyLink.swift */,
|
||||
);
|
||||
path = VerticalCombinationViews;
|
||||
sourceTree = "<group>";
|
||||
@ -532,8 +541,8 @@
|
||||
D29DF0E421E4F3C7003B2FB9 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D29DF2D021E7C1C8003B2FB9 /* MVMAnimationFramework.framework */,
|
||||
D29DF0E521E4F3C7003B2FB9 /* MVMCore.framework */,
|
||||
9455B19B234F8A0400A574DB /* MVMAnimationFramework.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -651,6 +660,7 @@
|
||||
D29DF2A021E7AF4E003B2FB9 /* MVMCoreUIUtility.m */,
|
||||
D29DF2A721E7B2F9003B2FB9 /* MVMCoreUIConstants.h */,
|
||||
D29DF2A821E7B2F9003B2FB9 /* MVMCoreUIConstants.m */,
|
||||
0A41BA6D2344FCD400D4C0BC /* CATransaction+Extension.swift */,
|
||||
);
|
||||
path = Utility;
|
||||
sourceTree = "<group>";
|
||||
@ -724,6 +734,8 @@
|
||||
DB891E822253FA8500022516 /* Label.swift */,
|
||||
0198F7A02256A80A0066C936 /* MFRadioButton.h */,
|
||||
0198F7A22256A80A0066C936 /* MFRadioButton.m */,
|
||||
0A7BAFA0232BE61800FB8E22 /* Checkbox.swift */,
|
||||
0A7BAFA2232BE63400FB8E22 /* CheckboxWithLabelView.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
@ -955,11 +967,12 @@
|
||||
D29DF0CB21E404D4003B2FB9 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
LastSwiftMigration = 1010;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = D29DF0C621E404D4003B2FB9 /* Build configuration list for PBXProject "MVMCoreUI" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
compatibilityVersion = "Xcode 8.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@ -1039,6 +1052,7 @@
|
||||
D2D6CD4022E78C1A00D701B8 /* Scroller.swift in Sources */,
|
||||
D29DF13021E6851E003B2FB9 /* MVMCoreUITopAlertShortView.m in Sources */,
|
||||
D28B4F8B21FF967C00712C7A /* MVMCoreUIObject.m in Sources */,
|
||||
0A1B4A96233BB18F005B3FB4 /* CheckboxWithLabelView.swift in Sources */,
|
||||
D260D7B222D65BDD007E7233 /* MVMCoreUIPageControl.m in Sources */,
|
||||
D29DF26D21E6AA0B003B2FB9 /* FLAnimatedImageView.m in Sources */,
|
||||
D29DF2EF21ECEAE1003B2FB9 /* MFFonts.m in Sources */,
|
||||
@ -1046,6 +1060,7 @@
|
||||
D282AACB2243C61700C46919 /* ButtonView.swift in Sources */,
|
||||
D2D6CD4222E78FAB00D701B8 /* ThreeLayerTemplate.swift in Sources */,
|
||||
0105618F224BBE7700E1557D /* FormValidator+FormParams.swift in Sources */,
|
||||
0A7BAFA1232BE61800FB8E22 /* Checkbox.swift in Sources */,
|
||||
D22479962316AF6E003FCCF9 /* HeadlineBodyTextButton.swift in Sources */,
|
||||
D2E1FADD2268B25E00AEFD8C /* MoleculeTableViewCell.swift in Sources */,
|
||||
D29DF2AE21E7B3A4003B2FB9 /* MFTextView.m in Sources */,
|
||||
@ -1073,6 +1088,7 @@
|
||||
D22D1F47220496A30077CEC0 /* MVMCoreUISwitch.m in Sources */,
|
||||
D29DF28C21E7AC2B003B2FB9 /* ViewConstrainingView.m in Sources */,
|
||||
D29DF17B21E69E1F003B2FB9 /* PrimaryButton.m in Sources */,
|
||||
D27CD4102339057800C1DC07 /* EyebrowHeadlineBodyLink.swift in Sources */,
|
||||
D29DF11D21E684A9003B2FB9 /* MVMCoreUISplitViewController.m in Sources */,
|
||||
0198F79F225679880066C936 /* FormValidationProtocol.swift in Sources */,
|
||||
D29DF29821E7ADB8003B2FB9 /* MFScrollingViewController.m in Sources */,
|
||||
@ -1089,6 +1105,7 @@
|
||||
D29DF25121E6A177003B2FB9 /* MFDigitTextBox.m in Sources */,
|
||||
DBC4391B224421A0001AB423 /* CaretButton.swift in Sources */,
|
||||
0198F7A82256A80B0066C936 /* MFRadioButton.m in Sources */,
|
||||
0A41BA6E2344FCD400D4C0BC /* CATransaction+Extension.swift in Sources */,
|
||||
D29DF13221E6851E003B2FB9 /* MVMCoreUITopAlertBaseView.m in Sources */,
|
||||
D29DF29C21E7ADB9003B2FB9 /* MFProgrammaticTableViewController.m in Sources */,
|
||||
0105618E224BBE7700E1557D /* FormValidator+TextFields.swift in Sources */,
|
||||
@ -1175,7 +1192,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
@ -1234,12 +1251,11 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.1;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
VERSION_INFO_PREFIX = "";
|
||||
@ -1261,12 +1277,8 @@
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks";
|
||||
INFOPLIST_FILE = MVMCoreUI/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
@ -1291,12 +1303,8 @@
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../SharedFrameworks";
|
||||
INFOPLIST_FILE = MVMCoreUI/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@loader_path/Frameworks",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.vzw.MVMCoreUI;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SKIP_INSTALL = YES;
|
||||
|
||||
@ -83,11 +83,9 @@
|
||||
self.enabled = YES;
|
||||
|
||||
// Disable SmartQuotes
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.textField.smartQuotesType = UITextSmartQuotesTypeNo;
|
||||
self.textField.smartDashesType = UITextSmartDashesTypeNo;
|
||||
self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
|
||||
}
|
||||
self.textField.smartQuotesType = UITextSmartQuotesTypeNo;
|
||||
self.textField.smartDashesType = UITextSmartDashesTypeNo;
|
||||
self.textField.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
436
MVMCoreUI/Atoms/Views/Checkbox.swift
Normal file
436
MVMCoreUI/Atoms/Views/Checkbox.swift
Normal file
@ -0,0 +1,436 @@
|
||||
//
|
||||
// Checkbox.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 9/13/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import MVMCore
|
||||
|
||||
/*
|
||||
!!! -- DO NOT REMOVE -- !!!
|
||||
(Unless Design changes the appearance of the checkmark).
|
||||
|
||||
// Offsets based on the 124x124 example checkmark
|
||||
let startXOffset: Float = 42.0 / 124.0 ~~ 0.33871
|
||||
let startYOffset: Float = 66.0 / 124.0 ~~ 0.53225
|
||||
let pivotXOffset: Float = 58.0 / 124.0 ~~ 0.46774
|
||||
let pivotYOffset: Float = 80.0 / 124.0 ~~ 0.64516
|
||||
let endXOffset: Float = 83.0 / 124.0 ~~ 0.66935
|
||||
let endYOffset: Float = 46.0 / 124.0 ~~ 0.37097
|
||||
let pivotPercentage: Float = 0.34
|
||||
let endPercentage = 1.0 - pivotPercentage
|
||||
let animationInterval: Float = 0.01
|
||||
*/
|
||||
|
||||
/**
|
||||
This class expects its height and width to be equal.
|
||||
*/
|
||||
@objcMembers open class Checkbox: UIControl, MVMCoreViewProtocol, MVMCoreUIMoleculeViewProtocol, MVMCoreUIViewConstrainingProtocol {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
// Form Validation
|
||||
var isRequired = false
|
||||
var fieldKey: String?
|
||||
var delegateObject: DelegateObject?
|
||||
|
||||
public static let defaultHeightWidth: CGFloat = 18.0
|
||||
|
||||
/// If true the border of this checkbox will be circular.
|
||||
public var isRound: Bool = false
|
||||
|
||||
/// Determined if the checkbox's UI should animated when selected.
|
||||
public var isAnimated: Bool = true
|
||||
|
||||
/// Disables all selection logic when setting the value of isSelected, reducing it to a stored property.
|
||||
public var updateSelectionOnly: Bool = false
|
||||
|
||||
/// The color of the background when checked.
|
||||
public var checkedBackgroundColor: UIColor = .white {
|
||||
didSet {
|
||||
if isSelected {
|
||||
backgroundColor = checkedBackgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The color of the background when unChecked.
|
||||
public var unCheckedBackgroundColor: UIColor = .white {
|
||||
didSet {
|
||||
if !isSelected {
|
||||
backgroundColor = unCheckedBackgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves ideeal radius value to curve square into a circle.
|
||||
public var cornerRadiusValue: CGFloat {
|
||||
return bounds.size.height / 2
|
||||
}
|
||||
|
||||
/// Action Block called when the switch is selected.
|
||||
public var actionBlock: ActionBlock?
|
||||
|
||||
/// Manages the appearance of the checkbox.
|
||||
private var shapeLayer: CAShapeLayer?
|
||||
|
||||
/// Width of the check mark.
|
||||
public var checkWidth: CGFloat = 2 {
|
||||
didSet {
|
||||
if let shapeLayer = shapeLayer {
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.lineWidth = checkWidth
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Color of the check mark.
|
||||
public var checkColor: UIColor = .black {
|
||||
didSet {
|
||||
if let shapeLayer = shapeLayer {
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.strokeColor = checkColor.cgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Border width of the checkbox
|
||||
public var borderWidth: CGFloat = 1 {
|
||||
didSet {
|
||||
layer.borderWidth = borderWidth
|
||||
}
|
||||
}
|
||||
|
||||
/// border color of the Checkbox
|
||||
public var borderColor: UIColor = .black {
|
||||
didSet {
|
||||
layer.borderColor = borderColor.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The represented state of the Checkbox.
|
||||
|
||||
Setting updateSelectionOnly to true bypasses the animation logic inherent with setting this property.
|
||||
*/
|
||||
override open var isSelected: Bool {
|
||||
didSet {
|
||||
if !updateSelectionOnly {
|
||||
layoutIfNeeded()
|
||||
shapeLayer?.removeAllAnimations()
|
||||
|
||||
updateCheckboxUI(isSelected: isSelected, isAnimated: isAnimated)
|
||||
|
||||
if let delegate = delegateObject as? FormValidationProtocol {
|
||||
delegate.formValidatorModel?()?.enableByValidation()
|
||||
}
|
||||
|
||||
updateAccessibilityLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
accessibilityTraits = .button
|
||||
accessibilityHint = MVMCoreUIUtility.hardcodedString(withKey: "checkbox_action_hint")
|
||||
updateAccessibilityLabel()
|
||||
|
||||
setupView()
|
||||
}
|
||||
|
||||
/// There is currently no intention on using xib files.
|
||||
required public init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
fatalError("xib file is not implemented for Checkbox.")
|
||||
}
|
||||
|
||||
public convenience init() {
|
||||
self.init(frame:.zero)
|
||||
}
|
||||
|
||||
public convenience init(isChecked: Bool) {
|
||||
self.init(frame: .zero)
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
public convenience init(checkedBackgroundColor: UIColor, unCheckedBackgroundColor: UIColor, isChecked: Bool = false) {
|
||||
self.init(frame: .zero)
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
self.checkedBackgroundColor = checkedBackgroundColor
|
||||
self.unCheckedBackgroundColor = unCheckedBackgroundColor
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Lifecycle
|
||||
//--------------------------------------------------
|
||||
|
||||
override open func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
drawCheck()
|
||||
layer.cornerRadius = isRound ? cornerRadiusValue : 0
|
||||
layer.borderWidth = borderWidth
|
||||
layer.borderColor = borderColor.cgColor
|
||||
}
|
||||
|
||||
open func setupView() {
|
||||
|
||||
isUserInteractionEnabled = true
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
backgroundColor = .white
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Actions
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func sendAction(_ action: Selector, to target: Any?, for event: UIEvent?) {
|
||||
super.sendAction(action, to: target, for: event)
|
||||
toggleAndAction()
|
||||
}
|
||||
|
||||
open override func sendActions(for controlEvents: UIControl.Event) {
|
||||
super.sendActions(for: controlEvents)
|
||||
toggleAndAction()
|
||||
}
|
||||
|
||||
/// This will toggle the state of the Checkbox and execute the actionBlock if provided.
|
||||
public func toggleAndAction() {
|
||||
|
||||
isSelected.toggle()
|
||||
actionBlock?()
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Creates the check mark used for the checkbox.
|
||||
private func drawCheck() {
|
||||
|
||||
if shapeLayer == nil {
|
||||
|
||||
let shapeLayer = CAShapeLayer()
|
||||
self.shapeLayer = shapeLayer
|
||||
shapeLayer.frame = bounds
|
||||
layer.addSublayer(shapeLayer)
|
||||
shapeLayer.strokeColor = checkColor.cgColor
|
||||
shapeLayer.fillColor = UIColor.clear.cgColor
|
||||
shapeLayer.path = checkMarkPath()
|
||||
shapeLayer.lineJoin = .miter
|
||||
shapeLayer.lineWidth = checkWidth
|
||||
|
||||
CATransaction.withDisabledAnimations {
|
||||
shapeLayer.strokeEnd = isSelected ? 1 : 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a UIBezierPath detailing the path of a checkmark
|
||||
func checkMarkPath() -> CGPath {
|
||||
|
||||
let sideLength = max(bounds.size.height, bounds.size.width)
|
||||
let startPoint = CGPoint(x: 0.33871 * sideLength, y: 0.53225 * sideLength)
|
||||
let pivotOffSet = CGPoint(x: 0.46774 * sideLength, y: 0.64516 * sideLength)
|
||||
let endOffset = CGPoint(x: 0.66935 * sideLength , y: 0.37097 * sideLength)
|
||||
|
||||
let bezierPath = UIBezierPath()
|
||||
bezierPath.move(to: startPoint)
|
||||
bezierPath.addLine(to: pivotOffSet)
|
||||
bezierPath.addLine(to: endOffset)
|
||||
|
||||
return bezierPath.cgPath
|
||||
}
|
||||
|
||||
/// Programmatic means to check/uncheck the box.
|
||||
/// - parameter selected: state of the check box: true = checked OR false = unchecked.
|
||||
/// - parameter animated: allows the state of the checkbox to change with or without animation.
|
||||
public func updateSelection(to selected: Bool, animated: Bool) {
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
self.updateSelectionOnly = true
|
||||
self.isSelected = selected
|
||||
self.updateSelectionOnly = false
|
||||
self.drawCheck()
|
||||
self.shapeLayer?.removeAllAnimations()
|
||||
self.updateCheckboxUI(isSelected: selected, isAnimated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
/// updates the visuals of the check mark and background.
|
||||
/// - parameter isSelection: the check state of the checkbox.
|
||||
/// - parameter isAnimated: determines of the changes should animate or immediately refelect.
|
||||
public func updateCheckboxUI(isSelected: Bool, isAnimated: Bool) {
|
||||
|
||||
if isAnimated {
|
||||
let animateStrokeEnd = CABasicAnimation(keyPath: "strokeEnd")
|
||||
animateStrokeEnd.timingFunction = CAMediaTimingFunction(name: .linear)
|
||||
animateStrokeEnd.duration = 0.3
|
||||
animateStrokeEnd.fillMode = .both
|
||||
animateStrokeEnd.isRemovedOnCompletion = false
|
||||
animateStrokeEnd.fromValue = !isSelected ? 1 : 0
|
||||
animateStrokeEnd.toValue = isSelected ? 1 : 0
|
||||
self.shapeLayer?.add(animateStrokeEnd, forKey: "strokeEnd")
|
||||
|
||||
UIView.animate(withDuration: 0.2, delay: 0.1, options: .curveEaseOut, animations: {
|
||||
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
|
||||
})
|
||||
} else {
|
||||
CATransaction.withDisabledAnimations {
|
||||
self.shapeLayer?.strokeEnd = isSelected ? 1 : 0
|
||||
}
|
||||
|
||||
self.backgroundColor = isSelected ? self.checkedBackgroundColor : self.unCheckedBackgroundColor
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust accessibility label based on state of Checkbox.
|
||||
func updateAccessibilityLabel() {
|
||||
// Attention: This needs to be addressed with the accessibility team.
|
||||
if let state = MVMCoreUIUtility.hardcodedString(withKey: isSelected ? "checkbox_checked_state" : "checkbox_unchecked_state") {
|
||||
accessibilityLabel = String(format: MVMCoreUIUtility.hardcodedString(withKey: "checkbox_desc_state") ?? "%@", state)
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - UITouch
|
||||
//--------------------------------------------------
|
||||
|
||||
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
|
||||
sendActions(for: touchIsAcceptablyOutside(touches.first) ? .touchUpOutside : .touchUpInside)
|
||||
}
|
||||
|
||||
func touchIsAcceptablyOutside(_ touch: UITouch?) -> Bool {
|
||||
let endLocation = touch?.location(in: self)
|
||||
let x = endLocation?.x ?? 0.0
|
||||
let y = endLocation?.y ?? 0.0
|
||||
let faultTolerance: CGFloat = 20.0
|
||||
let widthLimit = CGFloat(bounds.size.width + faultTolerance)
|
||||
let heightLimt = CGFloat(bounds.size.height + faultTolerance)
|
||||
|
||||
return x < -faultTolerance || y < -faultTolerance || x > widthLimit || y > heightLimt
|
||||
}
|
||||
|
||||
override open func accessibilityActivate() -> Bool {
|
||||
sendActions(for: .touchUpInside)
|
||||
return true
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Molecular
|
||||
//--------------------------------------------------
|
||||
|
||||
open func needsToBeConstrained() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
open func reset() {
|
||||
|
||||
shapeLayer?.removeAllAnimations()
|
||||
shapeLayer?.removeFromSuperlayer()
|
||||
shapeLayer = nil
|
||||
backgroundColor = nil
|
||||
borderColor = .black
|
||||
borderWidth = 1.0
|
||||
checkColor = .black
|
||||
checkWidth = 2.0
|
||||
updateSelectionOnly = true
|
||||
isSelected = false
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
open func setAsMolecule() {
|
||||
setupView()
|
||||
}
|
||||
|
||||
public func updateView(_ size: CGFloat) {
|
||||
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
public func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
self.delegateObject = delegateObject
|
||||
FormValidator.setupValidation(molecule: self, delegate: delegateObject?.formValidationProtocol)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let fieldKey = dictionary[KeyFieldKey] as? String {
|
||||
self.fieldKey = fieldKey
|
||||
}
|
||||
|
||||
if let isRequired = dictionary[KeyRequired] as? Bool {
|
||||
self.isRequired = isRequired
|
||||
}
|
||||
|
||||
if let borderColorHex = dictionary["borderColor"] as? String {
|
||||
layer.borderColor = UIColor.mfGet(forHex: borderColorHex).cgColor
|
||||
}
|
||||
|
||||
if let borderWidth = dictionary["borderWidth"] as? CGFloat {
|
||||
layer.borderWidth = borderWidth
|
||||
}
|
||||
|
||||
if let isChecked = dictionary["isChecked"] as? Bool, isChecked {
|
||||
updateSelectionOnly = true
|
||||
isSelected = isChecked
|
||||
updateSelectionOnly = false
|
||||
}
|
||||
|
||||
if let checkColorHex = dictionary["checkColor"] as? String {
|
||||
checkColor = UIColor.mfGet(forHex: checkColorHex)
|
||||
}
|
||||
|
||||
if let unCheckedBackgroundColorHex = dictionary["unCheckedBackgroundColor"] as? String {
|
||||
unCheckedBackgroundColor = UIColor.mfGet(forHex: unCheckedBackgroundColorHex)
|
||||
}
|
||||
|
||||
if let checkedBackgroundColorHex = dictionary["checkedBackgroundColor"] as? String {
|
||||
checkedBackgroundColor = UIColor.mfGet(forHex: checkedBackgroundColorHex)
|
||||
}
|
||||
|
||||
if let isAnimated = dictionary["isAnimated"] as? Bool {
|
||||
self.isAnimated = isAnimated
|
||||
}
|
||||
|
||||
if let isRound = dictionary["isRound"] as? Bool {
|
||||
self.isRound = isRound
|
||||
}
|
||||
|
||||
if let actionMap = dictionary.optionalDictionaryForKey("actionMap") {
|
||||
actionBlock = { MVMCoreActionHandler.shared()?.handleAction(with: actionMap, additionalData: additionalData, delegateObject: delegateObject) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- FormValidationProtocol
|
||||
extension Checkbox: FormValidationProtocol {
|
||||
|
||||
public func isValidField() -> Bool {
|
||||
return isRequired ? isSelected : true
|
||||
}
|
||||
|
||||
public func formFieldName() -> String? {
|
||||
return fieldKey
|
||||
}
|
||||
|
||||
public func formFieldValue() -> Any? {
|
||||
return NSNumber(value: isSelected)
|
||||
}
|
||||
}
|
||||
200
MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift
Normal file
200
MVMCoreUI/Atoms/Views/CheckboxWithLabelView.swift
Normal file
@ -0,0 +1,200 @@
|
||||
//
|
||||
// CheckboxWithLabelView.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 9/13/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
@objcMembers open class CheckboxWithLabelView: ViewConstrainingView {
|
||||
//--------------------------------------------------
|
||||
// MARK: - Outlets
|
||||
//--------------------------------------------------
|
||||
|
||||
public let checkbox = Checkbox()
|
||||
public let label = Label.commonLabelB2(true)
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Properties
|
||||
//--------------------------------------------------
|
||||
|
||||
var sizeObject: MFSizeObject? = MFSizeObject(standardSize: Checkbox.defaultHeightWidth, standardiPadPortraitSize: Checkbox.defaultHeightWidth + 6.0)
|
||||
|
||||
var checkboxPosition: CheckboxPosition = .center
|
||||
|
||||
public enum CheckboxPosition: String {
|
||||
case center
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Constraints
|
||||
//--------------------------------------------------
|
||||
|
||||
var checkboxWidthConstraint: NSLayoutConstraint?
|
||||
var checkboxHeightConstraint: NSLayoutConstraint?
|
||||
var checkboxTopConstraint: NSLayoutConstraint?
|
||||
var checkboxBottomConstraint: NSLayoutConstraint?
|
||||
var checkboxCenterYConstraint: NSLayoutConstraint?
|
||||
var centerLabelCheckboxConstraint: NSLayoutConstraint?
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Life Cycle
|
||||
//--------------------------------------------------
|
||||
|
||||
override open func setupView() {
|
||||
super.setupView()
|
||||
|
||||
guard subviews.isEmpty else { return }
|
||||
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
addSubview(checkbox)
|
||||
addSubview(label)
|
||||
|
||||
label.text = ""
|
||||
|
||||
let dimension = sizeObject?.getValueBasedOnApplicationWidth() ?? Checkbox.defaultHeightWidth
|
||||
checkboxWidthConstraint = checkbox.heightAnchor.constraint(equalToConstant: dimension)
|
||||
checkboxWidthConstraint?.isActive = true
|
||||
checkboxHeightConstraint = checkbox.widthAnchor.constraint(equalToConstant: dimension)
|
||||
checkboxHeightConstraint?.isActive = true
|
||||
|
||||
checkbox.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true
|
||||
checkbox.topAnchor.constraint(greaterThanOrEqualTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: checkbox.bottomAnchor).isActive = true
|
||||
|
||||
let checboxBottom = checkbox.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
|
||||
checboxBottom.priority = UILayoutPriority(249)
|
||||
checboxBottom.isActive = true
|
||||
|
||||
// Allows various positions of checkbox.
|
||||
checkboxBottomConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: checkbox.bottomAnchor)
|
||||
checkboxTopConstraint = checkbox.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor)
|
||||
checkboxCenterYConstraint = checkbox.centerYAnchor.constraint(equalTo: centerYAnchor)
|
||||
centerLabelCheckboxConstraint = label.centerYAnchor.constraint(equalTo: checkbox.centerYAnchor)
|
||||
|
||||
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true
|
||||
layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
|
||||
label.leadingAnchor.constraint(equalTo: checkbox.trailingAnchor, constant: PaddingTwo).isActive = true
|
||||
|
||||
layoutMarginsGuide.bottomAnchor.constraint(greaterThanOrEqualTo: label.bottomAnchor).isActive = true
|
||||
let bottomLabelConstraint = layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor)
|
||||
bottomLabelConstraint.priority = UILayoutPriority(249)
|
||||
bottomLabelConstraint.isActive = true
|
||||
|
||||
alignCheckbox(.center)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Initializers
|
||||
//--------------------------------------------------
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
fatalError("xib file is not implemented for CheckboxWithLabelView")
|
||||
}
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setupView()
|
||||
}
|
||||
|
||||
public convenience init() {
|
||||
self.init(frame: .zero)
|
||||
}
|
||||
|
||||
public convenience init(position: CheckboxPosition) {
|
||||
self.init(frame: .zero)
|
||||
|
||||
alignCheckbox(position)
|
||||
}
|
||||
|
||||
//--------------------------------------------------
|
||||
// MARK: - Methods
|
||||
//--------------------------------------------------
|
||||
|
||||
/// Aligns Checkbox and Label relative to the desired position of the Checkbox.
|
||||
private func alignCheckbox(_ position: CheckboxPosition) {
|
||||
checkboxPosition = position
|
||||
|
||||
switch position {
|
||||
case .center:
|
||||
checkboxBottomConstraint?.isActive = false
|
||||
checkboxTopConstraint?.isActive = false
|
||||
checkboxCenterYConstraint?.isActive = true
|
||||
centerLabelCheckboxConstraint?.isActive = true
|
||||
|
||||
case .top:
|
||||
checkboxBottomConstraint?.isActive = false
|
||||
checkboxTopConstraint?.isActive = true
|
||||
checkboxCenterYConstraint?.isActive = false
|
||||
centerLabelCheckboxConstraint?.isActive = false
|
||||
|
||||
case .bottom:
|
||||
checkboxBottomConstraint?.isActive = true
|
||||
checkboxTopConstraint?.isActive = false
|
||||
checkboxCenterYConstraint?.isActive = false
|
||||
centerLabelCheckboxConstraint?.isActive = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// MARK: - Molecular
|
||||
extension CheckboxWithLabelView {
|
||||
|
||||
override open class func estimatedHeight(forRow json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
return CGFloat(Checkbox.defaultHeightWidth)
|
||||
}
|
||||
|
||||
@objc override open func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
|
||||
label.updateView(size)
|
||||
|
||||
if self.checkbox.responds(to: #selector(self.updateView(_:))) {
|
||||
if let dimension = sizeObject?.getValueBased(onSize: size) {
|
||||
checkboxWidthConstraint?.constant = dimension
|
||||
checkboxHeightConstraint?.constant = dimension
|
||||
checkbox.updateView(size)
|
||||
}
|
||||
}
|
||||
|
||||
layoutIfNeeded()
|
||||
}
|
||||
|
||||
override open func alignment() -> UIStackView.Alignment {
|
||||
return .leading
|
||||
}
|
||||
|
||||
open override func resetConstraints() {
|
||||
super.resetConstraints()
|
||||
|
||||
checkboxCenterYConstraint?.isActive = false
|
||||
checkboxBottomConstraint?.isActive = false
|
||||
checkboxTopConstraint?.isActive = false
|
||||
centerLabelCheckboxConstraint?.isActive = false
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
|
||||
label.text = ""
|
||||
checkbox.reset()
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
|
||||
guard let dictionary = json else { return }
|
||||
|
||||
if let checkboxAlignment = dictionary["checkboxAlignment"] as? String, let position = CheckboxPosition(rawValue: checkboxAlignment) {
|
||||
alignCheckbox(position)
|
||||
}
|
||||
|
||||
checkbox.setWithJSON(dictionary.dictionaryForKey("checkbox"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
label.setWithJSON(dictionary.dictionaryForKey("label"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
}
|
||||
}
|
||||
@ -174,7 +174,7 @@ public typealias CoreObjectActionLoadPresentDelegate = MVMCoreActionDelegateProt
|
||||
super.init(frame: .zero)
|
||||
|
||||
setText(fullText, startTag: startTag, endTag: endTag)
|
||||
actionBlock = label?.createActionBlockFrom(actionMap: actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
setActionMap(actionMap, additionalData: additionalData, delegateObject: delegateObject)
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
|
||||
@ -173,11 +173,10 @@
|
||||
view.placeHolderLabel.textColor = [UIColor mfLightGrayColor];
|
||||
|
||||
// Disable SmartQuotes
|
||||
if (@available(iOS 11.0, *)) {
|
||||
view.textView.smartQuotesType = UITextSmartQuotesTypeNo;
|
||||
view.textView.smartDashesType = UITextSmartDashesTypeNo;
|
||||
view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
|
||||
}
|
||||
view.textView.smartQuotesType = UITextSmartQuotesTypeNo;
|
||||
view.textView.smartDashesType = UITextSmartDashesTypeNo;
|
||||
view.textView.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo;
|
||||
|
||||
[view didSetFont:view.textView.font];
|
||||
view.hideBorder = YES;
|
||||
return view;
|
||||
|
||||
@ -48,9 +48,7 @@
|
||||
|
||||
- (void)setAsMolecule {
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
self.insetsLayoutMarginsFromSafeArea = NO;
|
||||
}
|
||||
self.insetsLayoutMarginsFromSafeArea = NO;
|
||||
}
|
||||
|
||||
- (void)reset {
|
||||
|
||||
@ -306,6 +306,8 @@ static const CGFloat CheckBoxHeightWidth = 18.0;
|
||||
self.checkedSquare.layer.cornerRadius = self.isRoundRectCheckMark ? 5.0f : 0;
|
||||
}
|
||||
|
||||
// TODO:.....................................
|
||||
|
||||
#pragma mark - XIB Helpers
|
||||
|
||||
- (instancetype)awakeAfterUsingCoder:(NSCoder *)aDecoder {
|
||||
|
||||
@ -76,7 +76,7 @@ import UIKit
|
||||
override open func setupView() {
|
||||
super.setupView()
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
backgroundColor = .mfSilver()
|
||||
backgroundColor = .mfLightSilver()
|
||||
clipsToBounds = true
|
||||
if thicknessConstraint == nil {
|
||||
thicknessConstraint = heightAnchor.constraint(equalToConstant: defaultHeight)
|
||||
@ -84,6 +84,12 @@ import UIKit
|
||||
}
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
backgroundColor = .mfLightSilver()
|
||||
progressList = nil
|
||||
}
|
||||
|
||||
override open func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
thicknessConstraint?.constant = json?.optionalCGFloatForKey("thickness") ?? defaultHeight
|
||||
|
||||
@ -70,7 +70,7 @@ import Foundation
|
||||
thickness = 8
|
||||
progress = 0
|
||||
progressTintColor = UIColor.mfCerulean()
|
||||
trackTintColor = UIColor.mfSilver()
|
||||
trackTintColor = UIColor.mfLightSilver()
|
||||
}
|
||||
|
||||
public static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
|
||||
@ -34,6 +34,9 @@
|
||||
@property (nonatomic) BOOL updateViewHorizontalDefaults;
|
||||
@property (nonatomic) BOOL updateViewVerticalDefaults;
|
||||
|
||||
@property (nonatomic) CGFloat topMarginPadding;
|
||||
@property (nonatomic) CGFloat bottomMarginPadding;
|
||||
|
||||
/// A molecule if we constrain one.
|
||||
@property (weak, nullable, nonatomic) UIView <MVMCoreUIMoleculeViewProtocol>*molecule;
|
||||
|
||||
|
||||
@ -304,18 +304,15 @@
|
||||
[super setupView];
|
||||
self.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.topMarginPadding = PaddingDefaultVerticalSpacing3;
|
||||
self.bottomMarginPadding = PaddingDefaultVerticalSpacing3;
|
||||
[MVMCoreUIUtility setMarginsForView:self leading:0 top:0 trailing:0 bottom:0];
|
||||
}
|
||||
|
||||
- (void)updateView:(CGFloat)size {
|
||||
[super updateView:size];
|
||||
if ([self.constrainedView respondsToSelector:@selector(updateView:)]) {
|
||||
[((id<MVMCoreViewProtocol>)self.constrainedView) updateView:size];
|
||||
}
|
||||
if (self.molecule != self.constrainedView) {
|
||||
[self.molecule updateView:size];
|
||||
}
|
||||
[MFStyler setDefaultMarginsForView:self size:size horizontal:self.updateViewHorizontalDefaults vertical:self.updateViewVerticalDefaults];
|
||||
[self.molecule updateView:size];
|
||||
[MFStyler setMarginsForView:self size:size defaultHorizontal:self.updateViewHorizontalDefaults top:(self.updateViewVerticalDefaults ? self.topMarginPadding : 0) bottom:(self.updateViewVerticalDefaults ? self.bottomMarginPadding : 0)];
|
||||
UIEdgeInsets margins = [MVMCoreUIUtility getMarginsForView:self];
|
||||
if (self.updateViewHorizontalDefaults) {
|
||||
[self setLeftPinConstant:margins.left];
|
||||
@ -344,6 +341,8 @@
|
||||
[super reset];
|
||||
self.updateViewHorizontalDefaults = NO;
|
||||
self.updateViewVerticalDefaults = NO;
|
||||
self.topMarginPadding = PaddingDefaultVerticalSpacing3;
|
||||
self.bottomMarginPadding = PaddingDefaultVerticalSpacing3;
|
||||
if ([self.molecule respondsToSelector:@selector(alignment)]) {
|
||||
[self alignHorizontal:[(UIView <MVMCoreUIViewConstrainingProtocol> *)self.molecule alignment]];
|
||||
}
|
||||
@ -354,8 +353,10 @@
|
||||
}
|
||||
|
||||
- (void)setWithJSON:(NSDictionary *)json delegateObject:(MVMCoreUIDelegateObject *)delegateObject additionalData:(NSDictionary *)additionalData {
|
||||
[super setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
|
||||
|
||||
// Only treated as a container if we are constraining a molecule.
|
||||
if (!self.constrainedView) {
|
||||
[super setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
|
||||
}
|
||||
[self.molecule setWithJSON:json delegateObject:delegateObject additionalData:additionalData];
|
||||
if (self.shouldSetupMoleculeFromJSON) {
|
||||
NSDictionary *moleculeJSON = [json dict:KeyMolecule];
|
||||
|
||||
@ -61,12 +61,8 @@
|
||||
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
tableView.delegate = self;
|
||||
tableView.dataSource = self;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
tableView.insetsContentViewsToSafeArea = NO;
|
||||
}
|
||||
if ([tableView respondsToSelector:@selector(setCellLayoutMarginsFollowReadableWidth:)]) {
|
||||
tableView.cellLayoutMarginsFollowReadableWidth = NO;
|
||||
}
|
||||
tableView.insetsContentViewsToSafeArea = NO;
|
||||
tableView.cellLayoutMarginsFollowReadableWidth = NO;
|
||||
return tableView;
|
||||
}
|
||||
|
||||
|
||||
@ -91,10 +91,7 @@ static NSTimeInterval const HandScrollAnimationTiming = 7.f;
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
BOOL automaticInset = NO;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
|
||||
}
|
||||
BOOL automaticInset = self.navigationController && self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic;
|
||||
|
||||
// Takes into account the navigation bar.
|
||||
if (!automaticInset && (self.edgesForExtendedLayout & UIRectEdgeTop)) {
|
||||
|
||||
@ -527,6 +527,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
if (self.selectedField) {
|
||||
[self.selectedField resignFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self stopObservingForResponseJSONUpdates];
|
||||
MVMCoreLog(@"%@ deallocated", [[self class] description]);
|
||||
|
||||
@ -190,13 +190,9 @@ open class ThreeLayerTableViewController: MFProgrammaticTableViewController {
|
||||
footerView.topAnchor.constraint(equalTo: tableView.bottomAnchor).isActive = true
|
||||
footerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
|
||||
view.rightAnchor.constraint(equalTo: footerView.rightAnchor).isActive = true
|
||||
if #available(iOS 11.0, *) {
|
||||
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
|
||||
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
|
||||
safeAreaView?.backgroundColor = bottomView?.backgroundColor
|
||||
} else {
|
||||
view.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
|
||||
}
|
||||
view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: footerView.bottomAnchor).isActive = true
|
||||
safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: view)
|
||||
safeAreaView?.backgroundColor = bottomView?.backgroundColor
|
||||
} else {
|
||||
bottomConstraint?.isActive = true
|
||||
var y: CGFloat?
|
||||
|
||||
@ -45,7 +45,7 @@ open class ThreeLayerViewController: ProgrammaticScrollViewController {
|
||||
return
|
||||
}
|
||||
|
||||
if #available(iOS 11.0, *), scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
|
||||
if scrollView.contentInsetAdjustmentBehavior == UIScrollView.ContentInsetAdjustmentBehavior.automatic {
|
||||
heightConstraint?.constant = -scrollView.adjustedContentInset.top - scrollView.adjustedContentInset.bottom
|
||||
} else {
|
||||
heightConstraint?.constant = -scrollView.contentInset.top - scrollView.contentInset.bottom
|
||||
@ -233,14 +233,10 @@ extension ThreeLayerViewController {
|
||||
view.topAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
|
||||
NSLayoutConstraint.pinViewLeft(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
|
||||
NSLayoutConstraint.pinViewRight(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
|
||||
if #available(iOS 11.0, *) {
|
||||
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
|
||||
safeAreaView.backgroundColor = bottomView?.backgroundColor
|
||||
self.safeAreaView = safeAreaView
|
||||
}
|
||||
} else {
|
||||
NSLayoutConstraint.pinViewBottom(toSuperview: view, useMargins: useMargins, constant: 0).isActive = true
|
||||
parentView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
|
||||
if let safeAreaView = MVMCoreUICommonViewsUtility.getAndSetupSafeAreaView(on: parentView) {
|
||||
safeAreaView.backgroundColor = bottomView?.backgroundColor
|
||||
self.safeAreaView = safeAreaView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -283,16 +283,6 @@
|
||||
|
||||
// So we will update titles.
|
||||
[self newDataBuildScreen];
|
||||
|
||||
// Fix for right bar button item with custom view which disappears when user navigates to top tabbar page controller
|
||||
if (@available(iOS 11.0, *)) {
|
||||
} else {
|
||||
NSMutableArray *buttonItems = [[NSMutableArray alloc] init];
|
||||
UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:self action:nil];
|
||||
[buttonItems addObject:space];
|
||||
[buttonItems addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
self.navigationItem.rightBarButtonItems = buttonItems;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
|
||||
@ -325,19 +325,13 @@
|
||||
NSLayoutConstraint *bottomViewTop = [NSLayoutConstraint constraintWithItem:footerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:tableView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
|
||||
bottomViewTop.active = YES;
|
||||
|
||||
NSLayoutConstraint *bottomViewBot = nil;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
|
||||
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
|
||||
safeAreaView.backgroundColor = footerView.backgroundColor;
|
||||
self.safeAreaView = safeAreaView;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
bottomViewBot = [NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:footerView attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
|
||||
}
|
||||
bottomViewBot.priority = 900;
|
||||
bottomViewBot.active = YES;
|
||||
NSLayoutConstraint *bottomViewBot = [self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:footerView.bottomAnchor];
|
||||
bottomViewBot.priority = 900;
|
||||
bottomViewBot.active = YES;
|
||||
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
|
||||
safeAreaView.backgroundColor = footerView.backgroundColor;
|
||||
self.safeAreaView = safeAreaView;
|
||||
|
||||
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[footerView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(footerView)]];
|
||||
} else {
|
||||
|
||||
@ -272,13 +272,8 @@
|
||||
- (void)updateViewConstraints {
|
||||
[super updateViewConstraints];
|
||||
|
||||
// Updates for ios 11
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
|
||||
self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
|
||||
} else {
|
||||
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
|
||||
}
|
||||
if (self.scrollView.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
|
||||
self.heightConstraint.constant = -self.scrollView.adjustedContentInset.top - self.scrollView.adjustedContentInset.bottom;
|
||||
} else {
|
||||
self.heightConstraint.constant = -self.scrollView.contentInset.top - self.scrollView.contentInset.bottom;
|
||||
}
|
||||
@ -322,17 +317,13 @@
|
||||
[self.view addSubview:bottomView];
|
||||
|
||||
UIScrollView *scrollview = self.scrollView;
|
||||
if (@available(iOS 11.0, *)) {
|
||||
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
|
||||
[self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
|
||||
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
|
||||
safeAreaView.backgroundColor = bottomView.backgroundColor;
|
||||
self.safeAreaView = safeAreaView;
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]-0-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
|
||||
}
|
||||
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollview]-0-[bottomView]" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(scrollview,bottomView)]];
|
||||
[self.view.safeAreaLayoutGuide.bottomAnchor constraintEqualToAnchor:bottomView.bottomAnchor].active = YES;
|
||||
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility getAndSetupSafeAreaViewOnView:self.view];
|
||||
safeAreaView.backgroundColor = bottomView.backgroundColor;
|
||||
self.safeAreaView = safeAreaView;
|
||||
|
||||
[NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[bottomView]-0@900-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(bottomView)]];
|
||||
}
|
||||
|
||||
|
||||
@ -37,11 +37,9 @@ open class MoleculeCollectionViewCell: UICollectionViewCell, MVMCoreUIMoleculeVi
|
||||
}
|
||||
isAccessibilityElement = false
|
||||
contentView.isAccessibilityElement = false
|
||||
if #available(iOS 11.0, *) {
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
}
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
|
||||
// Covers the card when peaking.
|
||||
peakingCover.backgroundColor = .white
|
||||
|
||||
@ -87,29 +87,16 @@ import UIKit
|
||||
// MARK: - MFViewProtocol
|
||||
public func updateView(_ size: CGFloat) {
|
||||
MFStyler.setMarginsFor(self, size: size, defaultHorizontal: updateViewHorizontalDefaults, top: topMarginPadding, bottom: bottomMarginPadding)
|
||||
if #available(iOS 11.0, *) {
|
||||
if accessoryView != nil {
|
||||
// Smaller left margin if accessory view.
|
||||
var margin = directionalLayoutMargins
|
||||
margin.trailing = 16
|
||||
contentView.directionalLayoutMargins = margin
|
||||
} else {
|
||||
contentView.directionalLayoutMargins = directionalLayoutMargins
|
||||
}
|
||||
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
|
||||
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
|
||||
if accessoryView != nil {
|
||||
// Smaller left margin if accessory view.
|
||||
var margin = directionalLayoutMargins
|
||||
margin.trailing = 16
|
||||
contentView.directionalLayoutMargins = margin
|
||||
} else {
|
||||
if accessoryView != nil {
|
||||
// Smaller left margin if accessory view.
|
||||
var margin = layoutMargins
|
||||
margin.right = 16
|
||||
contentView.layoutMargins = margin
|
||||
} else {
|
||||
contentView.layoutMargins = layoutMargins
|
||||
}
|
||||
topSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
|
||||
bottomSeparatorView?.setLeftAndRightPinConstant(layoutMargins.left)
|
||||
contentView.directionalLayoutMargins = directionalLayoutMargins
|
||||
}
|
||||
topSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
|
||||
bottomSeparatorView?.setLeftAndRightPinConstant(directionalLayoutMargins.leading)
|
||||
|
||||
molecule?.updateView(size)
|
||||
if let _ = accessoryView, let caretView = caretView, let widthObject = caretViewWidthSizeObject, let heightObject = caretViewHeightSizeObject {
|
||||
@ -121,11 +108,9 @@ import UIKit
|
||||
|
||||
public func setupView() {
|
||||
selectionStyle = .none
|
||||
if #available(iOS 11.0, *) {
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
}
|
||||
insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.insetsLayoutMarginsFromSafeArea = false
|
||||
contentView.preservesSuperviewLayoutMargins = false
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
|
||||
@ -19,7 +19,8 @@ import UIKit
|
||||
}
|
||||
translatesAutoresizingMaskIntoConstraints = false
|
||||
scrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||
addConstrainedView(scrollView)
|
||||
addSubview(scrollView)
|
||||
pinView(toSuperView: scrollView)
|
||||
scrollView.addSubview(contentView)
|
||||
NSLayoutConstraint.constraintPinSubview(toSuperview: contentView)
|
||||
let constraint = contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor, multiplier: 1.0)
|
||||
|
||||
@ -11,6 +11,8 @@ import UIKit
|
||||
open class StandardFooterView: ViewConstrainingView {
|
||||
open override func setupView() {
|
||||
super.setupView()
|
||||
topMarginPadding = PaddingDefaultVerticalSpacing
|
||||
bottomMarginPadding = PaddingDefaultVerticalSpacing
|
||||
shouldSetupMoleculeFromJSON = true
|
||||
updateViewVerticalDefaults = true
|
||||
updateViewHorizontalDefaults = true
|
||||
|
||||
@ -22,6 +22,8 @@ public class StandardHeaderView: ViewConstrainingView {
|
||||
shouldSetupMoleculeFromJSON = true
|
||||
updateViewVerticalDefaults = true
|
||||
updateViewHorizontalDefaults = true
|
||||
topMarginPadding = PaddingDefaultVerticalSpacing
|
||||
bottomMarginPadding = PaddingDefaultVerticalSpacing
|
||||
if separatorView == nil, let separatorView = SeparatorView.separatorAdd(to: self, position: SeparatorPositionBot, withHorizontalPadding: 0) {
|
||||
separatorView.setAsHeavy()
|
||||
addSubview(separatorView)
|
||||
@ -54,6 +56,8 @@ public class StandardHeaderView: ViewConstrainingView {
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
topMarginPadding = PaddingDefaultVerticalSpacing
|
||||
bottomMarginPadding = PaddingDefaultVerticalSpacing
|
||||
separatorView?.setAsHeavy()
|
||||
separatorView?.show()
|
||||
}
|
||||
|
||||
@ -0,0 +1,70 @@
|
||||
//
|
||||
// EyebrowHeadlineBodyLink.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Scott Pfeil on 9/23/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers open class EyebrowHeadlineBodyLink: ViewConstrainingView {
|
||||
let stack = MoleculeStackView(frame: .zero)
|
||||
let eyebrow = Label.commonLabelB3(true)
|
||||
let headline = Label.commonLabelB1(true)
|
||||
let body = Label.commonLabelB2(true)
|
||||
let link = MFTextButton(nil, constrainHeight: false, forWidth: MVMCoreUIUtility.getWidth())
|
||||
|
||||
// MARK: - MFViewProtocol
|
||||
open override func setupView() {
|
||||
super.setupView()
|
||||
guard stack.superview == nil else {
|
||||
return
|
||||
}
|
||||
stack.spacing = 0
|
||||
stack.updateViewHorizontalDefaults = false
|
||||
addSubview(stack)
|
||||
pinView(toSuperView: stack)
|
||||
stack.addStackItem(StackItem(with: eyebrow), lastItem: false)
|
||||
stack.addStackItem(StackItem(with: headline), lastItem: false)
|
||||
stack.addStackItem(StackItem(with: body), lastItem: false)
|
||||
|
||||
// To visually take into account the extra padding in the intrinsic content of a button.
|
||||
let stackItem = StackItem(with: link)
|
||||
stackItem.spacing = -6
|
||||
stack.addStackItem(stackItem, lastItem: true)
|
||||
}
|
||||
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
super.updateView(size)
|
||||
stack.updateView(size)
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreUIMoleculeViewProtocol
|
||||
open override func setWithJSON(_ json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable : Any]?) {
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
eyebrow.setWithJSON(json?.optionalDictionaryForKey("eyebrow"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
stack.items[0].gone = !eyebrow.hasText
|
||||
headline.setWithJSON(json?.optionalDictionaryForKey("headline"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
stack.items[1].gone = !headline.hasText
|
||||
body.setWithJSON(json?.optionalDictionaryForKey("body"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
stack.items[2].gone = !body.hasText
|
||||
link.setWithJSON(json?.optionalDictionaryForKey("link"), delegateObject: delegateObject, additionalData: additionalData)
|
||||
stack.items[3].gone = link.titleLabel?.text?.count ?? 0 == 0
|
||||
stack.restack()
|
||||
}
|
||||
|
||||
open override func reset() {
|
||||
super.reset()
|
||||
stack.reset()
|
||||
stack.spacing = 0
|
||||
stack.updateViewHorizontalDefaults = false
|
||||
eyebrow.styleB3(true)
|
||||
headline.styleB1(true)
|
||||
body.styleB2(true)
|
||||
}
|
||||
|
||||
public override static func estimatedHeight(forRow json: [AnyHashable : Any]?, delegateObject: MVMCoreUIDelegateObject?) -> CGFloat {
|
||||
return 65
|
||||
}
|
||||
}
|
||||
@ -32,6 +32,8 @@ open class HeadlineBody: ViewConstrainingView {
|
||||
stylePageHeader()
|
||||
case "item":
|
||||
styleListItem()
|
||||
case "itemHeader":
|
||||
styleListItemDivider()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
@ -53,6 +55,12 @@ open class HeadlineBody: ViewConstrainingView {
|
||||
messageLabel.styleB2(true)
|
||||
spaceBetweenLabelsConstant = 0
|
||||
}
|
||||
|
||||
func styleListItemDivider() {
|
||||
headlineLabel.styleH3(true)
|
||||
messageLabel.styleB2(true)
|
||||
spaceBetweenLabelsConstant = 0
|
||||
}
|
||||
|
||||
// MARK: - MVMCoreViewProtocol
|
||||
open override func updateView(_ size: CGFloat) {
|
||||
@ -80,10 +88,9 @@ open class HeadlineBody: ViewConstrainingView {
|
||||
|
||||
headlineLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
|
||||
messageLabel.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
|
||||
setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
|
||||
view.setContentHuggingPriority(UILayoutPriority.required, for: NSLayoutConstraint.Axis.vertical)
|
||||
|
||||
topPin = headlineLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
|
||||
topPin?.isActive = true
|
||||
headlineLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
|
||||
|
||||
spaceBetweenLabels = messageLabel.topAnchor.constraint(equalTo: headlineLabel.bottomAnchor, constant: spaceBetweenLabelsConstant)
|
||||
spaceBetweenLabels?.isActive = true
|
||||
@ -100,8 +107,7 @@ open class HeadlineBody: ViewConstrainingView {
|
||||
rightConstraintMessage = view.rightAnchor.constraint(equalTo: messageLabel.rightAnchor)
|
||||
rightConstraintMessage?.isActive = true
|
||||
|
||||
bottomPin = view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 0)
|
||||
bottomPin?.isActive = true
|
||||
view.bottomAnchor.constraint(equalTo: messageLabel.bottomAnchor, constant: 0).isActive = true
|
||||
}
|
||||
|
||||
// MARK: - Constraining
|
||||
|
||||
@ -14,25 +14,27 @@ public class StackItem {
|
||||
var percentage: Int?
|
||||
var verticalAlignment: UIStackView.Alignment?
|
||||
var horizontalAlignment: UIStackView.Alignment?
|
||||
var gone = false
|
||||
|
||||
init(with view: UIView) {
|
||||
self.view = view
|
||||
}
|
||||
|
||||
init(with view: UIView, json: [AnyHashable: Any]) {
|
||||
init(with view: UIView, json: [AnyHashable: Any]?) {
|
||||
self.view = view
|
||||
update(with: json)
|
||||
}
|
||||
|
||||
func update(with json: [AnyHashable: Any]) {
|
||||
spacing = json.optionalCGFloatForKey("spacing")
|
||||
percentage = json["percent"] as? Int
|
||||
if let alignment = json.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"verticalAlignment"]) {
|
||||
func update(with json: [AnyHashable: Any]?) {
|
||||
gone = json?.boolForKey("gone") ?? (json == nil)
|
||||
spacing = json?.optionalCGFloatForKey("spacing")
|
||||
percentage = json?["percent"] as? Int
|
||||
if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"verticalAlignment"]) {
|
||||
verticalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill)
|
||||
} else {
|
||||
verticalAlignment = nil
|
||||
}
|
||||
if let alignment = json.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"horizontalAlignment"]) {
|
||||
if let alignment = json?.stringOptionalWithChainOfKeysOrIndexes([KeyMolecule,"horizontalAlignment"]) {
|
||||
horizontalAlignment = ViewConstrainingView.getAlignmentFor(alignment, defaultAlignment: .fill)
|
||||
} else {
|
||||
horizontalAlignment = nil
|
||||
@ -84,11 +86,13 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
|
||||
/// Restacks the existing items.
|
||||
func restack() {
|
||||
MVMCoreUIStackableViewController.remove(contentView.subviews)
|
||||
let stackItems = items
|
||||
items.removeAll()
|
||||
for (index, item) in stackItems.enumerated() {
|
||||
addStackItem(item, lastItem: index == stackItems.count - 1)
|
||||
setWithStackItems(items)
|
||||
}
|
||||
|
||||
/// Removes all stack items views from the view.
|
||||
func removeAllItemViews() {
|
||||
for item in items {
|
||||
item.view.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +148,7 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
open override func setWithJSON(_ json: [AnyHashable: Any]?, delegateObject: MVMCoreUIDelegateObject?, additionalData: [AnyHashable: Any]?) {
|
||||
let previousJSON = self.json
|
||||
super.setWithJSON(json, delegateObject: delegateObject, additionalData: additionalData)
|
||||
MVMCoreUIStackableViewController.remove(contentView.subviews)
|
||||
removeAllItemViews()
|
||||
|
||||
// If the items in the stack are the same, just update previous items instead of re-allocating.
|
||||
var items: [StackItem]?
|
||||
@ -238,6 +242,10 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
|
||||
/// Adds the stack item to the stack.
|
||||
func addStackItem(_ stackItem: StackItem, lastItem: Bool) {
|
||||
guard !stackItem.gone else {
|
||||
items.append(stackItem)
|
||||
return
|
||||
}
|
||||
let view = stackItem.view
|
||||
contentView.addSubview(view)
|
||||
view.translatesAutoresizingMaskIntoConstraints = false
|
||||
@ -249,10 +257,13 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
view.alignHorizontal?(horizontalAlignment)
|
||||
view.alignVertical?(verticalAlignment)
|
||||
}
|
||||
let first = items.first { !$0.gone } == nil
|
||||
if axis == .vertical {
|
||||
if items.count == 0 {
|
||||
if first {
|
||||
pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : stackItem.spacing ?? 0)
|
||||
} else if let previousView = items.last?.view {
|
||||
} else if let previousView = items.last(where: { stackItem in
|
||||
return !stackItem.gone
|
||||
})?.view {
|
||||
_ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: true)
|
||||
}
|
||||
pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: 0)
|
||||
@ -264,10 +275,12 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
pinView(contentView, toView: view, attribute: .bottom, relation: .equal, priority: .required, constant: 0)
|
||||
}
|
||||
} else {
|
||||
if items.count == 0 {
|
||||
if first {
|
||||
// First horizontal item has no spacing by default unless told otherwise.
|
||||
pinView(view, toView: contentView, attribute: .leading, relation: .equal, priority: .required, constant: useStackSpacingBeforeFirstItem ? spacing : stackItem.spacing ?? 0)
|
||||
} else if let previousView = items.last?.view {
|
||||
} else if let previousView = items.last(where: { stackItem in
|
||||
return !stackItem.gone
|
||||
})?.view {
|
||||
_ = NSLayoutConstraint(pinFirstView: previousView, toSecondView: view, withConstant: spacing, directionVertical: false)
|
||||
}
|
||||
pinView(view, toView: contentView, attribute: .top, relation: .equal, priority: .required, constant: 0)
|
||||
@ -281,4 +294,20 @@ public class MoleculeStackView: ViewConstrainingView {
|
||||
}
|
||||
items.append(stackItem)
|
||||
}
|
||||
|
||||
func setWithStackItems(_ items: [StackItem]) {
|
||||
removeAllItemViews()
|
||||
self.items.removeAll()
|
||||
var previousPresentItem: StackItem? = nil
|
||||
for item in items {
|
||||
if !item.gone {
|
||||
previousPresentItem = item
|
||||
}
|
||||
addStackItem(item, lastItem: false)
|
||||
}
|
||||
if let lastView = previousPresentItem?.view {
|
||||
let attribute: NSLayoutConstraint.Attribute = axis == .vertical ? .bottom : .right
|
||||
pinView(contentView, toView: lastView, attribute: attribute, relation: .equal, priority: .required, constant: 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,11 +38,11 @@
|
||||
@"caretButton": CaretButton.class,
|
||||
@"textField" : MFTextField.class,
|
||||
@"digitTextField" : MFDigitTextField.class,
|
||||
@"checkbox" : MVMCoreUICheckBox.class,
|
||||
@"checkbox" : Checkbox.class,
|
||||
@"checkboxWithLabelView" : CheckboxWithLabelView.class,
|
||||
@"cornerLabels" : CornerLabels.class,
|
||||
@"progressBar": ProgressBar.class,
|
||||
@"multiProgressBar": MultiProgress.class,
|
||||
@"checkbox": MVMCoreUICheckBox.class,
|
||||
@"listItem": MoleculeTableViewCell.class,
|
||||
@"accordionListItem": AccordionMoleculeTableViewCell.class,
|
||||
@"switch": MVMCoreUISwitch.class,
|
||||
@ -61,7 +61,8 @@
|
||||
@"headlineBodyTextButton": HeadlineBodyTextButton.class,
|
||||
@"headlineBodyTextButtonSwitch": HeadlineBodyTextButtonSwitch.class,
|
||||
@"tabsListItem": TabsTableViewCell.class,
|
||||
@"HeadlineBodyButton": HeadlineBodyButton.class
|
||||
@"headlineBodyButton": HeadlineBodyButton.class,
|
||||
@"eyebrowHeadlineBodyLink": EyebrowHeadlineBodyLink.class
|
||||
} mutableCopy];
|
||||
});
|
||||
return mapping;
|
||||
|
||||
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal file
21
MVMCoreUI/Utility/CATransaction+Extension.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// CATransaction+Extension.swift
|
||||
// MVMCoreUI
|
||||
//
|
||||
// Created by Kevin Christiano on 10/2/19.
|
||||
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
extension CATransaction {
|
||||
|
||||
/// Performs changes without activating animation actions.
|
||||
static func withDisabledAnimations(_ actionBlock: ActionBlock) {
|
||||
CATransaction.begin()
|
||||
CATransaction.setDisableActions(true)
|
||||
actionBlock()
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
@ -198,13 +198,8 @@ static const CGFloat VertialShadowOffset = 6;
|
||||
[view.rightAnchor constraintEqualToAnchor:button.rightAnchor constant:PaddingTwo].active = YES;
|
||||
[view.centerYAnchor constraintEqualToAnchor:button.centerYAnchor].active = YES;
|
||||
} else {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
[button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES;
|
||||
[view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
|
||||
} else {
|
||||
[NSLayoutConstraint constraintPinSubview:button pinTop:YES topConstant:PaddingOne pinBottom:NO bottomConstant:0 pinLeft:NO leftConstant:0 pinRight:YES rightConstant:PaddingTwo];
|
||||
}
|
||||
|
||||
[button.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.topAnchor constant:PaddingOne].active = YES;
|
||||
[view.safeAreaLayoutGuide.trailingAnchor constraintEqualToAnchor:button.trailingAnchor constant:PaddingTwo].active = YES;
|
||||
}
|
||||
}
|
||||
return button;
|
||||
@ -249,16 +244,12 @@ static const CGFloat VertialShadowOffset = 6;
|
||||
}
|
||||
|
||||
+ (nullable UIView *)getAndSetupSafeAreaViewOnView:(nonnull UIView *)view {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView];
|
||||
[view addSubview:safeAreaView];
|
||||
[safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES;
|
||||
[view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES;
|
||||
[NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
|
||||
return safeAreaView;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
UIView *safeAreaView = [MVMCoreUICommonViewsUtility commonView];
|
||||
[view addSubview:safeAreaView];
|
||||
[safeAreaView.topAnchor constraintEqualToAnchor:view.safeAreaLayoutGuide.bottomAnchor].active = YES;
|
||||
[view.bottomAnchor constraintEqualToAnchor:safeAreaView.bottomAnchor].active = YES;
|
||||
[NSLayoutConstraint constraintPinSubview:safeAreaView pinTop:NO topConstant:0 pinBottom:NO bottomConstant:0 pinLeft:YES leftConstant:0 pinRight:YES rightConstant:0];
|
||||
return safeAreaView;
|
||||
}
|
||||
|
||||
#pragma mark - shadows
|
||||
|
||||
@ -58,21 +58,13 @@
|
||||
}
|
||||
|
||||
+ (UIEdgeInsets)getMarginsForView:(nullable UIView *)view {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
|
||||
} else {
|
||||
return view.layoutMargins;
|
||||
}
|
||||
return UIEdgeInsetsMake(view.directionalLayoutMargins.top, view.directionalLayoutMargins.leading, view.directionalLayoutMargins.bottom, view.directionalLayoutMargins.trailing);
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
|
||||
+ (void)setMarginsForView:(nullable UIView *)view leading:(CGFloat)leading top:(CGFloat)top trailing:(CGFloat)trailing bottom:(CGFloat)bottom {
|
||||
if (@available(iOS 11.0, *)) {
|
||||
view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
|
||||
} else {
|
||||
view.layoutMargins = UIEdgeInsetsMake(top, leading, bottom, trailing);
|
||||
}
|
||||
view.directionalLayoutMargins = NSDirectionalEdgeInsetsMake(top, leading, bottom, trailing);
|
||||
}
|
||||
|
||||
#pragma mark - Formatting
|
||||
@ -152,12 +144,9 @@
|
||||
CGFloat topInset = scrollview.contentInset.top;
|
||||
CGFloat bottomInset = scrollview.contentInset.bottom;
|
||||
|
||||
// Updates for ios 11
|
||||
if (@available(iOS 11.0, *)) {
|
||||
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
|
||||
topInset = scrollview.adjustedContentInset.top;
|
||||
bottomInset = scrollview.adjustedContentInset.bottom;
|
||||
}
|
||||
if (scrollview.contentInsetAdjustmentBehavior == UIScrollViewContentInsetAdjustmentAutomatic) {
|
||||
topInset = scrollview.adjustedContentInset.top;
|
||||
bottomInset = scrollview.adjustedContentInset.bottom;
|
||||
}
|
||||
|
||||
CGFloat remainingSpace = frameHeight - contentSizeHeight - topInset - bottomInset;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user