mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-26 11:30:50 +02:00
Merge branch 'develop' into riot_3396
This commit is contained in:
@@ -96,7 +96,7 @@
|
||||
|
||||
self.defaultIdentityServerUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"identityserverurl"];
|
||||
|
||||
self.welcomeImageView.image = [UIImage imageNamed:@"logo"];
|
||||
self.welcomeImageView.image = [UIImage imageNamed:@"horizontal_logo"];
|
||||
|
||||
[self.submitButton.layer setCornerRadius:5];
|
||||
self.submitButton.clipsToBounds = YES;
|
||||
@@ -172,6 +172,8 @@
|
||||
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
self.authenticationScrollView.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
self.welcomeImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// Style the authentication fallback webview screen so that its header matches to navigation bar style
|
||||
self.authFallbackContentView.backgroundColor = ThemeService.shared.theme.baseColor;
|
||||
@@ -223,6 +225,8 @@
|
||||
|
||||
self.softLogoutClearDataLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.softLogoutClearDataButton.backgroundColor = ThemeService.shared.theme.warningColor;
|
||||
|
||||
self.customServersTickButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[self.authInputsView customizeViewRendering];
|
||||
|
||||
@@ -1108,8 +1112,10 @@
|
||||
MXKAccount *account = [[MXKAccountManager sharedManager] accountForUserId:userId];
|
||||
MXSession *session = account.mxSession;
|
||||
|
||||
BOOL botCreationEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableBotCreation"];
|
||||
|
||||
// Create DM with Riot-bot on new account creation.
|
||||
if (self.authType == MXKAuthenticationTypeRegister)
|
||||
if (self.authType == MXKAuthenticationTypeRegister && botCreationEnabled)
|
||||
{
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters parametersForDirectRoomWithUser:@"@riot-bot:matrix.org"];
|
||||
[session createRoomWithParameters:roomCreationParameters success:nil failure:^(NSError *error) {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina5_9" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina5_9" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -91,12 +89,11 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rhx-dD-4EJ" userLabel="Content View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="logo" translatesAutoresizingMaskIntoConstraints="NO" id="d8r-TX-pwX" userLabel="Welcome Image View">
|
||||
<rect key="frame" x="127.66666666666669" y="25" width="120" height="101"/>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="horizontal_logo" translatesAutoresizingMaskIntoConstraints="NO" id="d8r-TX-pwX" userLabel="Welcome Image View">
|
||||
<rect key="frame" x="82.666666666666686" y="35" width="210" height="100"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCWelcomeImageView"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="120" id="ZFx-Mn-Kzq"/>
|
||||
<constraint firstAttribute="height" constant="101" id="zA1-WN-LdU"/>
|
||||
<constraint firstAttribute="height" constant="100" id="URJ-HW-UuW"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="xWb-IJ-v7F" userLabel="AuthInputsContainerView">
|
||||
@@ -430,7 +427,7 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="top" secondItem="xWb-IJ-v7F" secondAttribute="bottom" id="aGH-m3-xL3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="xWb-IJ-v7F" secondAttribute="trailing" id="hko-ol-XDd"/>
|
||||
<constraint firstItem="xWb-IJ-v7F" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" constant="160" id="khR-Uj-OTH"/>
|
||||
<constraint firstItem="d8r-TX-pwX" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" constant="25" id="l68-Ta-YKg"/>
|
||||
<constraint firstItem="d8r-TX-pwX" firstAttribute="top" secondItem="rhx-dD-4EJ" secondAttribute="top" constant="35" id="l68-Ta-YKg"/>
|
||||
<constraint firstAttribute="centerX" secondItem="d8r-TX-pwX" secondAttribute="centerX" id="l6k-EH-Yb8"/>
|
||||
<constraint firstItem="Gg0-TE-OGb" firstAttribute="leading" secondItem="rhx-dD-4EJ" secondAttribute="leading" id="rS3-go-zbf"/>
|
||||
</constraints>
|
||||
@@ -510,7 +507,7 @@ Clear it if you're finished using this device, or want to sign in to another acc
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="logo" width="120" height="101"/>
|
||||
<image name="horizontal_logo" width="210" height="120"/>
|
||||
<image name="selection_untick" width="22" height="22"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -163,6 +163,9 @@
|
||||
self.cancelButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
_bugReportDescriptionTextView.layer.borderColor = ThemeService.shared.theme.headerBackgroundColor.CGColor;
|
||||
|
||||
self.sendLogsButtonImage.tintColor = ThemeService.shared.theme.tintColor;
|
||||
self.sendScreenshotButtonImage.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[self setNeedsStatusBarAppearanceUpdate];
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
@property (weak, nonatomic) IBOutlet UIView *gradientMaskContainerView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *chatButton;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *callControlsBackgroundView;
|
||||
|
||||
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *callerImageViewWidthConstraint;
|
||||
|
||||
// At the end of call, this flag indicates if the prompt to use the fallback should be displayed
|
||||
|
||||
@@ -64,27 +64,61 @@
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.backToAppButton setImage:[UIImage imageNamed:@"back_icon"] forState:UIControlStateNormal];
|
||||
[self.backToAppButton setImage:[UIImage imageNamed:@"back_icon"] forState:UIControlStateHighlighted];
|
||||
UIColor *unselectedColor = ThemeService.shared.theme.tabBarUnselectedItemTintColor;
|
||||
UIColor *selectedColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[self.cameraSwitchButton setImage:[UIImage imageNamed:@"camera_switch"] forState:UIControlStateNormal];
|
||||
[self.cameraSwitchButton setImage:[UIImage imageNamed:@"camera_switch"] forState:UIControlStateHighlighted];
|
||||
// Back button
|
||||
|
||||
[self.audioMuteButton setImage:[UIImage imageNamed:@"call_audio_mute_off_icon"] forState:UIControlStateNormal];
|
||||
[self.audioMuteButton setImage:[UIImage imageNamed:@"call_audio_mute_off_icon"] forState:UIControlStateHighlighted];
|
||||
[self.audioMuteButton setImage:[UIImage imageNamed:@"call_audio_mute_on_icon"] forState:UIControlStateSelected];
|
||||
[self.videoMuteButton setImage:[UIImage imageNamed:@"call_video_mute_off_icon"] forState:UIControlStateNormal];
|
||||
[self.videoMuteButton setImage:[UIImage imageNamed:@"call_video_mute_off_icon"] forState:UIControlStateHighlighted];
|
||||
[self.videoMuteButton setImage:[UIImage imageNamed:@"call_video_mute_on_icon"] forState:UIControlStateSelected];
|
||||
[self.speakerButton setImage:[UIImage imageNamed:@"call_speaker_off_icon"] forState:UIControlStateNormal];
|
||||
[self.speakerButton setImage:[UIImage imageNamed:@"call_speaker_on_icon"] forState:UIControlStateSelected];
|
||||
[self.chatButton setImage:[UIImage imageNamed:@"call_chat_icon"] forState:UIControlStateNormal];
|
||||
[self.chatButton setImage:[UIImage imageNamed:@"call_chat_icon"] forState:UIControlStateHighlighted];
|
||||
UIImage *backButtonImage = [[UIImage imageNamed:@"back_icon"] vc_tintedImageUsingColor:selectedColor];
|
||||
[self.backToAppButton setImage:backButtonImage forState:UIControlStateNormal];
|
||||
[self.backToAppButton setImage:backButtonImage forState:UIControlStateHighlighted];
|
||||
|
||||
// Camera switch
|
||||
|
||||
UIImage *cameraSwitchButtonImage = [[UIImage imageNamed:@"camera_switch"] vc_tintedImageUsingColor:selectedColor];
|
||||
[self.cameraSwitchButton setImage:cameraSwitchButtonImage forState:UIControlStateNormal];
|
||||
[self.cameraSwitchButton setImage:cameraSwitchButtonImage forState:UIControlStateHighlighted];
|
||||
|
||||
// Audio mute
|
||||
|
||||
UIImage *audioMuteOffButtonImage = [[UIImage imageNamed:@"call_audio_mute_off_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
|
||||
UIImage *audioMuteOnButtonImage = [[UIImage imageNamed:@"call_audio_mute_on_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
|
||||
[self.audioMuteButton setImage:audioMuteOffButtonImage forState:UIControlStateNormal];
|
||||
[self.audioMuteButton setImage:audioMuteOffButtonImage forState:UIControlStateHighlighted];
|
||||
[self.audioMuteButton setImage:audioMuteOnButtonImage forState:UIControlStateSelected];
|
||||
|
||||
// Video mute
|
||||
|
||||
UIImage *videoOffButtonImage = [[UIImage imageNamed:@"call_video_mute_off_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
UIImage *videoOnButtonImage = [[UIImage imageNamed:@"call_video_mute_on_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
|
||||
[self.videoMuteButton setImage:videoOffButtonImage forState:UIControlStateNormal];
|
||||
[self.videoMuteButton setImage:videoOffButtonImage forState:UIControlStateHighlighted];
|
||||
[self.videoMuteButton setImage:videoOnButtonImage forState:UIControlStateSelected];
|
||||
|
||||
// Speaker
|
||||
|
||||
UIImage *speakerOffButtonImage = [[UIImage imageNamed:@"call_speaker_off_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
UIImage *speakerOnButtonImage = [[UIImage imageNamed:@"call_speaker_on_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
[self.speakerButton setImage:speakerOffButtonImage forState:UIControlStateNormal];
|
||||
[self.speakerButton setImage:speakerOnButtonImage forState:UIControlStateSelected];
|
||||
|
||||
// Chat
|
||||
|
||||
UIImage *chatButtonImage = [[UIImage imageNamed:@"call_chat_icon"] vc_tintedImageUsingColor:unselectedColor];
|
||||
[self.chatButton setImage:chatButtonImage forState:UIControlStateNormal];
|
||||
[self.chatButton setImage:chatButtonImage forState:UIControlStateHighlighted];
|
||||
|
||||
// Hang up
|
||||
|
||||
UIImage *hangUpButtonImage = [[UIImage imageNamed:@"call_hangup_large"] vc_tintedImageUsingColor:ThemeService.shared.theme.noticeColor];
|
||||
|
||||
[self.endCallButton setTitle:nil forState:UIControlStateNormal];
|
||||
[self.endCallButton setTitle:nil forState:UIControlStateHighlighted];
|
||||
[self.endCallButton setImage:[UIImage imageNamed:@"call_hangup_icon"] forState:UIControlStateNormal];
|
||||
[self.endCallButton setImage:[UIImage imageNamed:@"call_hangup_icon"] forState:UIControlStateHighlighted];
|
||||
[self.endCallButton setImage:hangUpButtonImage forState:UIControlStateNormal];
|
||||
[self.endCallButton setImage:hangUpButtonImage forState:UIControlStateHighlighted];
|
||||
|
||||
// Define caller image view size
|
||||
CGSize size = [[UIScreen mainScreen] bounds].size;
|
||||
@@ -143,6 +177,8 @@
|
||||
// it seems only being supported on Mac OS.
|
||||
// so viewDidLayoutSubviews will refresh the layout bounds.
|
||||
[self.gradientMaskContainerView.layer addSublayer:gradientMaskLayer];
|
||||
|
||||
self.callControlsBackgroundView.backgroundColor = ThemeService.shared.theme.baseColor;
|
||||
}
|
||||
|
||||
- (BOOL)prefersStatusBarHidden
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -15,6 +15,7 @@
|
||||
<outlet property="backToAppButton" destination="nff-fB-sTq" id="UEn-bg-xuA"/>
|
||||
<outlet property="callContainerView" destination="W2P-Hy-nV2" id="b0q-Lj-daE"/>
|
||||
<outlet property="callControlContainerView" destination="nk9-Un-LVP" id="ZV3-qr-BtW"/>
|
||||
<outlet property="callControlsBackgroundView" destination="rSg-mB-C4P" id="9ga-Pa-Clo"/>
|
||||
<outlet property="callStatusLabel" destination="29y-MK-OWH" id="Kcn-MX-MUH"/>
|
||||
<outlet property="callerImageView" destination="v1I-LH-wvv" id="dpZ-KD-4Hg"/>
|
||||
<outlet property="callerImageViewWidthConstraint" destination="PAF-29-Cis" id="Crz-nE-p4c"/>
|
||||
@@ -39,7 +40,7 @@
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bOl-aO-r5f">
|
||||
@@ -53,7 +54,7 @@
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCRejectCallButton"/>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v1I-LH-wvv" customClass="MXKImageView">
|
||||
<rect key="frame" x="10" y="77" width="300" height="300"/>
|
||||
<rect key="frame" x="57" y="208.5" width="300" height="300"/>
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCCallerImageView"/>
|
||||
<constraints>
|
||||
@@ -62,17 +63,17 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tjb-57-yB1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCRemotePreviewContainerView"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="STO-VX-5VW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCGradientMaskContainerView"/>
|
||||
</view>
|
||||
<view hidden="YES" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="6gQ-zo-2Zw">
|
||||
<rect key="frame" x="20" y="388" width="79" height="106"/>
|
||||
<rect key="frame" x="20" y="464" width="79" height="106"/>
|
||||
<subviews>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" hidesWhenStopped="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="Bhz-hS-5B6">
|
||||
<rect key="frame" x="21" y="34.5" width="37" height="37"/>
|
||||
@@ -90,13 +91,17 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<view opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JAR-tn-sGN" userLabel="Overlay Container View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rSg-mB-C4P">
|
||||
<rect key="frame" x="0.0" y="764" width="414" height="88"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nk9-Un-LVP" userLabel="Call Control Container View">
|
||||
<rect key="frame" x="0.0" y="514" width="320" height="54"/>
|
||||
<rect key="frame" x="0.0" y="764" width="414" height="54"/>
|
||||
<subviews>
|
||||
<button autoresizesSubviews="NO" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fxP-zM-kfT">
|
||||
<rect key="frame" x="10" y="0.0" width="44" height="54"/>
|
||||
<rect key="frame" x="10" y="0.0" width="63" height="54"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCAudioMuteButton"/>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="13"/>
|
||||
@@ -113,7 +118,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Yq6-d2-Ea2">
|
||||
<rect key="frame" x="74" y="0.0" width="44" height="54"/>
|
||||
<rect key="frame" x="93" y="0.0" width="62.5" height="54"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCSpeakerButton"/>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="13"/>
|
||||
@@ -130,10 +135,10 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lVK-d8-Dqf" userLabel="End Call Button">
|
||||
<rect key="frame" x="138" y="0.0" width="44" height="54"/>
|
||||
<rect key="frame" x="175.5" y="0.0" width="63" height="54"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCEndCallButton"/>
|
||||
<state key="normal" image="call_hangup_icon">
|
||||
<state key="normal" image="call_hangup_large">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="selected" image="call_hangup_icon"/>
|
||||
@@ -143,7 +148,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="UHM-u9-ODN">
|
||||
<rect key="frame" x="202" y="0.0" width="44" height="54"/>
|
||||
<rect key="frame" x="258.5" y="0.0" width="62.5" height="54"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCVideoMuteButton"/>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="13"/>
|
||||
@@ -161,8 +166,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xCi-hD-FBs">
|
||||
<rect key="frame" x="266" y="0.0" width="44" height="54"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<rect key="frame" x="341" y="0.0" width="63" height="54"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCChatButton"/>
|
||||
<fontDescription key="fontDescription" name="Helvetica-Bold" family="Helvetica" pointSize="13"/>
|
||||
<inset key="contentEdgeInsets" minX="0.0" minY="0.0" maxX="4" maxY="0.0"/>
|
||||
@@ -179,7 +183,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCCallControlContainerView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="UHM-u9-ODN" firstAttribute="top" secondItem="nk9-Un-LVP" secondAttribute="top" id="9WM-jp-uJ2"/>
|
||||
@@ -206,17 +210,17 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W2P-Hy-nV2" userLabel="Call Container View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="60"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Name" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="IW8-8P-mS3">
|
||||
<rect key="frame" x="0.0" y="10" width="320" height="22"/>
|
||||
<rect key="frame" x="0.0" y="10" width="414" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCCallerNameLabel"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="-" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="12" translatesAutoresizingMaskIntoConstraints="NO" id="29y-MK-OWH">
|
||||
<rect key="frame" x="0.0" y="37" width="320" height="15"/>
|
||||
<rect key="frame" x="0.0" y="37" width="414" height="15"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCCallStatusLabel"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
@@ -257,7 +261,7 @@
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Iiz-W1-oNW">
|
||||
<rect key="frame" x="266" y="5" width="44" height="44"/>
|
||||
<rect key="frame" x="360" y="5" width="44" height="44"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCCameraSwitchButton"/>
|
||||
<constraints>
|
||||
@@ -283,9 +287,10 @@
|
||||
<accessibility key="accessibilityConfiguration" identifier="CallVCOverlayContainerView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="nk9-Un-LVP" firstAttribute="leading" secondItem="JAR-tn-sGN" secondAttribute="leading" id="9wq-Y2-kZa"/>
|
||||
<constraint firstItem="rSg-mB-C4P" firstAttribute="top" secondItem="nk9-Un-LVP" secondAttribute="top" id="A5j-LU-fJy"/>
|
||||
<constraint firstItem="nff-fB-sTq" firstAttribute="top" secondItem="JAR-tn-sGN" secondAttribute="top" constant="5" id="C4F-59-09O"/>
|
||||
<constraint firstItem="W2P-Hy-nV2" firstAttribute="leading" secondItem="JAR-tn-sGN" secondAttribute="leading" id="Dow-OM-vNZ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="nk9-Un-LVP" secondAttribute="bottom" id="bpc-m3-acb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="rSg-mB-C4P" secondAttribute="bottom" id="FYo-fo-DFI"/>
|
||||
<constraint firstAttribute="trailing" secondItem="W2P-Hy-nV2" secondAttribute="trailing" id="hPw-CT-hcl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Iiz-W1-oNW" secondAttribute="trailing" constant="10" id="hze-F3-Pyo"/>
|
||||
<constraint firstItem="nff-fB-sTq" firstAttribute="leading" secondItem="JAR-tn-sGN" secondAttribute="leading" constant="10" id="ubP-7v-uDn"/>
|
||||
@@ -302,11 +307,11 @@
|
||||
<constraint firstItem="v1I-LH-wvv" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" multiplier="0.8" id="7bF-kf-k3o"/>
|
||||
<constraint firstItem="r1a-fi-tZ0" firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="6gQ-zo-2Zw" secondAttribute="trailing" constant="20" id="9Om-6f-K9u"/>
|
||||
<constraint firstAttribute="bottom" secondItem="STO-VX-5VW" secondAttribute="bottom" id="DhB-zy-SJA"/>
|
||||
<constraint firstItem="JAR-tn-sGN" firstAttribute="bottom" secondItem="r1a-fi-tZ0" secondAttribute="bottom" id="PTN-1F-1xw"/>
|
||||
<constraint firstItem="JAR-tn-sGN" firstAttribute="leading" secondItem="r1a-fi-tZ0" secondAttribute="leading" id="QLG-jw-Xph"/>
|
||||
<constraint firstItem="6gQ-zo-2Zw" firstAttribute="leading" secondItem="r1a-fi-tZ0" secondAttribute="leading" priority="750" constant="20" id="Qvg-FG-sBr"/>
|
||||
<constraint firstItem="STO-VX-5VW" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="Tyh-Du-Ztt"/>
|
||||
<constraint firstItem="JAR-tn-sGN" firstAttribute="top" secondItem="r1a-fi-tZ0" secondAttribute="top" id="U4J-cG-hhC"/>
|
||||
<constraint firstItem="r1a-fi-tZ0" firstAttribute="bottom" secondItem="nk9-Un-LVP" secondAttribute="bottom" id="VpW-QU-xiw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="STO-VX-5VW" secondAttribute="trailing" id="X0Q-UU-v3g"/>
|
||||
<constraint firstItem="r1a-fi-tZ0" firstAttribute="trailing" secondItem="JAR-tn-sGN" secondAttribute="trailing" id="Xcc-jT-zbd"/>
|
||||
<constraint firstItem="v1I-LH-wvv" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="ZfI-qE-l2V"/>
|
||||
@@ -314,17 +319,21 @@
|
||||
<constraint firstAttribute="bottom" secondItem="Tjb-57-yB1" secondAttribute="bottom" id="akt-pO-kWM"/>
|
||||
<constraint firstItem="STO-VX-5VW" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="e4c-aG-jOB"/>
|
||||
<constraint firstItem="r1a-fi-tZ0" firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="6gQ-zo-2Zw" secondAttribute="bottom" constant="74" id="jCm-3K-6ah"/>
|
||||
<constraint firstItem="rSg-mB-C4P" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="jIP-1d-YfA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rSg-mB-C4P" secondAttribute="trailing" id="lVr-Iy-0mW"/>
|
||||
<constraint firstItem="Tjb-57-yB1" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="mW3-RA-hx2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Tjb-57-yB1" secondAttribute="trailing" id="rXn-dm-69F"/>
|
||||
<constraint firstItem="6gQ-zo-2Zw" firstAttribute="top" relation="greaterThanOrEqual" secondItem="r1a-fi-tZ0" secondAttribute="top" constant="80" id="tLa-Lx-XbY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="JAR-tn-sGN" secondAttribute="bottom" id="vNS-wR-TjW"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="r1a-fi-tZ0"/>
|
||||
<point key="canvasLocation" x="140" y="153"/>
|
||||
<point key="canvasLocation" x="139.13043478260872" y="152.67857142857142"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="back_icon" width="13.5" height="23"/>
|
||||
<image name="call_hangup_icon" width="24.5" height="26"/>
|
||||
<image name="camera_switch" width="31.5" height="23.5"/>
|
||||
<image name="call_hangup_large" width="40" height="40"/>
|
||||
<image name="camera_switch" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -89,22 +89,24 @@ static const CGFloat kButtonSize = 80.0;
|
||||
|
||||
UIColor *answerButtonBorderColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_icon"]
|
||||
self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangon_icon"]
|
||||
borderColor:answerButtonBorderColor];
|
||||
self.answerButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
self.answerButton.tintColor = answerButtonBorderColor;
|
||||
[self.answerButton addTarget:self action:@selector(didTapAnswerButton) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.answerTitleLabel = [[UILabel alloc] init];
|
||||
self.answerTitleLabel.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
self.answerTitleLabel.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
self.answerTitleLabel.textColor = answerButtonBorderColor;
|
||||
self.answerTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular];
|
||||
self.answerTitleLabel.text = NSLocalizedStringFromTable(@"accept", @"Vector", nil);
|
||||
|
||||
UIColor *rejectButtonBorderColor = ThemeService.shared.theme.warningColor;
|
||||
|
||||
self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"call_hangup_icon"]
|
||||
self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_hangup_icon"]
|
||||
borderColor:rejectButtonBorderColor];
|
||||
self.rejectButton.defaultBackgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
self.rejectButton.tintColor = rejectButtonBorderColor;
|
||||
[self.rejectButton addTarget:self action:@selector(didTapRejectButton) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.rejectTitleLabel = [[UILabel alloc] init];
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class Animation: NSObject, CAAnimationDelegate {
|
||||
/// Key frame animations which animate the properties of `layer`.
|
||||
fileprivate var keyframeAnimations: [CAKeyframeAnimation]
|
||||
|
||||
/// The CALayer whose properties are animated.
|
||||
open var layer: CALayer
|
||||
|
||||
/// Specifies whether or not the animation should autoreverse.
|
||||
var autoreverses: Bool
|
||||
|
||||
/// Determines the number of times the animation will repeat.
|
||||
///
|
||||
/// May be fractional. If the repeatCount is 0, it is ignored.
|
||||
/// Setting this property to greatestFiniteMagnitude will cause the animation to repeat forever.
|
||||
var repeatCount: Float
|
||||
|
||||
weak var delegate: AnimationDelegate?
|
||||
|
||||
/// The current time of the animation. i.e. what time is being displayed.
|
||||
var time: TimeInterval {
|
||||
return layer.timeOffset
|
||||
}
|
||||
|
||||
/// True if the animation is playing.
|
||||
var playing: Bool {
|
||||
return layer.speed != 0.0
|
||||
}
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init(layer: CALayer, keyframeAnimations: [CAKeyframeAnimation], autoreverses: Bool = false, repeatCount: Float = 0) {
|
||||
self.layer = layer
|
||||
self.keyframeAnimations = keyframeAnimations
|
||||
self.autoreverses = autoreverses
|
||||
self.repeatCount = repeatCount
|
||||
|
||||
super.init()
|
||||
keyframeAnimations.forEach(configure)
|
||||
reset()
|
||||
}
|
||||
|
||||
private func configure(keyframeAnimation: CAKeyframeAnimation) {
|
||||
keyframeAnimation.delegate = self
|
||||
keyframeAnimation.isRemovedOnCompletion = false
|
||||
keyframeAnimation.fillMode = .both
|
||||
keyframeAnimation.autoreverses = autoreverses
|
||||
keyframeAnimation.repeatCount = repeatCount
|
||||
}
|
||||
|
||||
// MARK: - Playing & Resetting Animation
|
||||
|
||||
/// Resumes the animation from where it was paused.
|
||||
open func play() {
|
||||
let pausedTime = layer.timeOffset
|
||||
layer.speed = 1.0
|
||||
layer.timeOffset = 0.0
|
||||
layer.beginTime = 0.0
|
||||
let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
|
||||
layer.beginTime = timeSincePause
|
||||
}
|
||||
|
||||
/// Pauses the animation.
|
||||
open func pause() {
|
||||
let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
|
||||
offset(to: pausedTime)
|
||||
}
|
||||
|
||||
/// Resets the animation to time 0.
|
||||
open func reset() {
|
||||
CATransaction.suppressAnimations {
|
||||
layer.removeAllAnimations()
|
||||
layer.beginTime = 0
|
||||
offset(to: 0)
|
||||
|
||||
for keyframeAnimation in keyframeAnimations {
|
||||
layer.setValue(keyframeAnimation.values?.first, forKeyPath: keyframeAnimation.keyPath!)
|
||||
}
|
||||
|
||||
addAllAnimations()
|
||||
layer.speed = 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds all the animations to `layer` so they can be played.
|
||||
private func addAllAnimations() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let keyframeAnimations = self?.keyframeAnimations, let layer = self?.layer else {
|
||||
return
|
||||
}
|
||||
for keyframeAnimation in keyframeAnimations {
|
||||
layer.add(keyframeAnimation, forKey: keyframeAnimation.keyPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Driving Animation
|
||||
|
||||
/// Shows the animation at time `time`.
|
||||
open func offset(to time: TimeInterval) {
|
||||
layer.speed = 0.0
|
||||
layer.timeOffset = time
|
||||
}
|
||||
|
||||
// MARK: - CAAnimationDelegate
|
||||
|
||||
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
||||
guard flag else {
|
||||
return
|
||||
}
|
||||
|
||||
let time = autoreverses ? 0 : (keyframeAnimations.first?.duration ?? 0)
|
||||
offset(to: time)
|
||||
|
||||
if let keyframeAnimation = anim as? CAKeyframeAnimation,
|
||||
keyframeAnimations.first?.keyPath == keyframeAnimation.keyPath {
|
||||
delegate?.didStop(animation: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Animation {
|
||||
var reversed: Animation {
|
||||
let reversedKeyFrameAnimations = keyframeAnimations.map { $0.reversed }
|
||||
return Animation(layer: layer, keyframeAnimations: reversedKeyFrameAnimations)
|
||||
}
|
||||
}
|
||||
|
||||
protocol AnimationDelegate: class {
|
||||
func didStop(animation: Animation)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public extension CAKeyframeAnimation {
|
||||
var reversed: CAKeyframeAnimation {
|
||||
let reversed = CAKeyframeAnimation(keyPath: keyPath)
|
||||
reversed.keyTimes = keyTimes?.reversed().map { NSNumber(floatLiteral: 1.0 - $0.doubleValue) }
|
||||
reversed.values = values?.reversed()
|
||||
reversed.timingFunctions = timingFunctions?.reversed().map { $0.reversed }
|
||||
reversed.duration = duration
|
||||
return reversed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public extension CAMediaTimingFunction {
|
||||
static let linear = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
static let easeIn = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
|
||||
static let easeOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
static let easeInEaseOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
|
||||
var reversed: CAMediaTimingFunction {
|
||||
let (c1, c2) = controlPoints
|
||||
let x1 = Float(1 - c2.x)
|
||||
let y1 = Float(1 - c2.y)
|
||||
let x2 = Float(1 - c1.x)
|
||||
let y2 = Float(1 - c1.y)
|
||||
return CAMediaTimingFunction(controlPoints: x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
var controlPoints: (CGPoint, CGPoint) {
|
||||
var c1: [Float] = [0, 0]
|
||||
var c2: [Float] = [0, 0]
|
||||
getControlPoint(at: 1, values: &c1)
|
||||
getControlPoint(at: 2, values: &c2)
|
||||
|
||||
let c1x = CGFloat(c1[0])
|
||||
let c1y = CGFloat(c1[1])
|
||||
let c2x = CGFloat(c2[0])
|
||||
let c2y = CGFloat(c2[1])
|
||||
|
||||
return (CGPoint(x: c1x, y: c1y), CGPoint(x: c2x, y: c2y))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CATransaction {
|
||||
static public func suppressAnimations(actions: () -> Void) {
|
||||
begin()
|
||||
setAnimationDuration(0)
|
||||
actions()
|
||||
commit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import QuartzCore
|
||||
|
||||
public func CGPathCreateWithSVGString(_ string: String) -> CGPath? {
|
||||
let parser = SVGPathStringParser()
|
||||
return try? parser.parse(string)
|
||||
}
|
||||
|
||||
class SVGPathStringParser {
|
||||
enum Error: Swift.Error {
|
||||
case invalidSyntax
|
||||
case missingValues
|
||||
}
|
||||
|
||||
let path = CGMutablePath()
|
||||
var currentPoint = CGPoint()
|
||||
var lastControlPoint = CGPoint()
|
||||
|
||||
var command: UnicodeScalar?
|
||||
var values = [CGFloat]()
|
||||
|
||||
func parse(_ string: String) throws -> CGPath {
|
||||
let tokens = SVGPathStringTokenizer(string: string).tokenize()
|
||||
|
||||
for token in tokens {
|
||||
switch token {
|
||||
case .command(let c):
|
||||
command = c
|
||||
values.removeAll()
|
||||
|
||||
case .value(let v):
|
||||
values.append(v)
|
||||
}
|
||||
|
||||
do {
|
||||
try addCommand()
|
||||
} catch Error.missingValues {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
fileprivate func addCommand() throws {
|
||||
guard let command = command else {
|
||||
return
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "M":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
path.move(to: CGPoint(x: values[0], y: values[1]))
|
||||
currentPoint = CGPoint(x: values[0], y: values[1])
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "m":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.move(to: point)
|
||||
currentPoint.x += values[0]
|
||||
currentPoint.y += values[1]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "L":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: values[0], y: values[1])
|
||||
path.addLine(to: point)
|
||||
currentPoint = CGPoint(x: values[0], y: values[1])
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "l":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.addLine(to: point)
|
||||
currentPoint.x += values[0]
|
||||
currentPoint.y += values[1]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "H":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: values[0], y: currentPoint.y)
|
||||
path.addLine(to: point)
|
||||
currentPoint.x = values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "h":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y)
|
||||
path.addLine(to: point)
|
||||
currentPoint.x += values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "V":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x, y: values[0])
|
||||
path.addLine(to: point)
|
||||
currentPoint.y = values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "v":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x, y: currentPoint.y + values[0])
|
||||
path.addLine(to: point)
|
||||
currentPoint.y += values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "C":
|
||||
if values.count < 6 { throw Error.missingValues }
|
||||
let c1 = CGPoint(x: values[0], y: values[1])
|
||||
let c2 = CGPoint(x: values[2], y: values[3])
|
||||
let to = CGPoint(x: values[4], y: values[5])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(6)
|
||||
|
||||
case "c":
|
||||
if values.count < 6 { throw Error.missingValues }
|
||||
let c1 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let c2 = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
let to = CGPoint(x: currentPoint.x + values[4], y: currentPoint.y + values[5])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(6)
|
||||
|
||||
case "S":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
var c1 = CGPoint()
|
||||
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let c2 = CGPoint(x: values[0], y: values[1])
|
||||
let to = CGPoint(x: values[2], y: values[3])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "s":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
var c1 = CGPoint()
|
||||
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let c2 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "Q":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
let control = CGPoint(x: values[0], y: values[1])
|
||||
let to = CGPoint(x: values[2], y: values[3])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "q":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
let control = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "T":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
var control = CGPoint()
|
||||
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let to = CGPoint(x: values[0], y: values[1])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(2)
|
||||
|
||||
case "t":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
var control = CGPoint()
|
||||
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let to = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(2)
|
||||
|
||||
case "Z", "z":
|
||||
path.closeSubpath()
|
||||
self.command = nil
|
||||
|
||||
default:
|
||||
throw Error.invalidSyntax
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SVGPathStringTokenizer {
|
||||
enum Token {
|
||||
case command(UnicodeScalar)
|
||||
case value(CGFloat)
|
||||
}
|
||||
|
||||
let separatorCharacterSet = CharacterSet(charactersIn: " \t\r\n,")
|
||||
let commandCharacterSet = CharacterSet(charactersIn: "mMLlHhVvzZCcSsQqTt")
|
||||
let numberStartCharacterSet = CharacterSet(charactersIn: "-+.0123456789")
|
||||
let numberCharacterSet = CharacterSet(charactersIn: ".0123456789eE")
|
||||
|
||||
var string: String
|
||||
var range: Range<String.UnicodeScalarView.Index>
|
||||
|
||||
init(string: String) {
|
||||
self.string = string
|
||||
range = string.unicodeScalars.startIndex..<string.unicodeScalars.endIndex
|
||||
}
|
||||
|
||||
func tokenize() -> [Token] {
|
||||
var array = [Token]()
|
||||
while let token = nextToken() {
|
||||
array.append(token)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
func nextToken() -> Token? {
|
||||
skipSeparators()
|
||||
|
||||
guard let c = get() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if commandCharacterSet.isMember(c) {
|
||||
return .command(c)
|
||||
}
|
||||
|
||||
if numberStartCharacterSet.isMember(c) {
|
||||
var numberString = String(c)
|
||||
while let c = peek(), numberCharacterSet.isMember(c) {
|
||||
numberString += String(c)
|
||||
get()
|
||||
}
|
||||
|
||||
if let value = Double(numberString) {
|
||||
return .value(CGFloat(value))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func get() -> UnicodeScalar? {
|
||||
guard range.lowerBound != range.upperBound else {
|
||||
return nil
|
||||
}
|
||||
let c = string.unicodeScalars[range.lowerBound]
|
||||
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
|
||||
return c
|
||||
}
|
||||
|
||||
func peek() -> UnicodeScalar? {
|
||||
guard range.lowerBound != range.upperBound else {
|
||||
return nil
|
||||
}
|
||||
return string.unicodeScalars[range.lowerBound]
|
||||
}
|
||||
|
||||
func skipSeparators() {
|
||||
while range.lowerBound != range.upperBound && separatorCharacterSet.isMember(string.unicodeScalars[range.lowerBound]) {
|
||||
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CharacterSet {
|
||||
public func isMember(_ c: UnicodeScalar) -> Bool {
|
||||
return contains(UnicodeScalar(c.value)!)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension NSMutableParagraphStyle {
|
||||
convenience init(alignment: NSTextAlignment,
|
||||
firstLineHeadIndent: CGFloat,
|
||||
headIndent: CGFloat,
|
||||
tailIndent: CGFloat,
|
||||
lineHeightMultiple: CGFloat,
|
||||
maximumLineHeight: CGFloat,
|
||||
minimumLineHeight: CGFloat,
|
||||
lineSpacing: CGFloat,
|
||||
paragraphSpacing: CGFloat,
|
||||
paragraphSpacingBefore: CGFloat) {
|
||||
self.init()
|
||||
self.alignment = alignment
|
||||
self.firstLineHeadIndent = firstLineHeadIndent
|
||||
self.headIndent = headIndent
|
||||
self.tailIndent = tailIndent
|
||||
self.lineHeightMultiple = lineHeightMultiple
|
||||
self.maximumLineHeight = maximumLineHeight
|
||||
self.minimumLineHeight = minimumLineHeight
|
||||
self.lineSpacing = lineSpacing
|
||||
self.paragraphSpacing = paragraphSpacing
|
||||
self.paragraphSpacingBefore = paragraphSpacingBefore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension NSShadow {
|
||||
convenience init(blurRadius: CGFloat, offset: CGSize, color: UIColor) {
|
||||
self.init()
|
||||
shadowBlurRadius = blurRadius
|
||||
shadowOffset = offset
|
||||
shadowColor = color
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright © 2016-2019 JABT
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class ShapeView: UIView {
|
||||
open var shapeLayer: CAShapeLayer {
|
||||
return (layer as? CAShapeLayer)!
|
||||
}
|
||||
|
||||
/// A sublayer which can be used to apply a gradient fill to `self`.
|
||||
open var gradientLayer: CAGradientLayer? {
|
||||
set {
|
||||
// Remove old gradient layer
|
||||
if let gradientLayer = gradientLayer {
|
||||
gradientLayer.removeFromSuperlayer()
|
||||
}
|
||||
// Replace old gradient with new one
|
||||
if let newGradientLayer = newValue {
|
||||
layer.addSublayer(newGradientLayer)
|
||||
}
|
||||
}
|
||||
|
||||
get {
|
||||
return layer.sublayers?.first(where: { $0 is CAGradientLayer }) as? CAGradientLayer
|
||||
}
|
||||
}
|
||||
|
||||
public func addGradient(type: CAGradientLayerType, startPoint: CGPoint, endPoint: CGPoint, stops: [(color: CGColor, location: NSNumber)]) {
|
||||
let gradientLayer = CAGradientLayer()
|
||||
gradientLayer.frame = shapeLayer.bounds
|
||||
self.gradientLayer = gradientLayer
|
||||
|
||||
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = shapeLayer.path
|
||||
mask.fillColor = UIColor.black.cgColor
|
||||
mask.strokeColor = nil
|
||||
|
||||
gradientLayer.startPoint = startPoint
|
||||
gradientLayer.endPoint = endPoint
|
||||
gradientLayer.colors = stops.map { $0.color }
|
||||
gradientLayer.locations = stops.map { $0.location }
|
||||
gradientLayer.type = type
|
||||
gradientLayer.frame = shapeLayer.bounds
|
||||
gradientLayer.mask = mask
|
||||
}
|
||||
|
||||
open var path: CGPath? {
|
||||
get {
|
||||
return shapeLayer.path
|
||||
}
|
||||
set {
|
||||
shapeLayer.path = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override open class var layerClass: AnyClass {
|
||||
return CAShapeLayer.self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import AVFoundation
|
||||
|
||||
public final class Sound {
|
||||
|
||||
static func playAudio(_ audio: AVAudioPlayer, delay: TimeInterval) {
|
||||
audio.prepareToPlay()
|
||||
let time = DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
|
||||
DispatchQueue.main.asyncAfter(deadline: time) {
|
||||
audio.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016-2019 JABT
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class TextView: UILabel {
|
||||
open var textLayer: CATextLayer {
|
||||
return (layer as? CATextLayer)!
|
||||
}
|
||||
|
||||
override open class var layerClass: AnyClass {
|
||||
return CATextLayer.self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
open class Timeline {
|
||||
public var view: UIView
|
||||
public var duration: TimeInterval
|
||||
public let animations: [Animation]
|
||||
public let sounds: [(sound: AVAudioPlayer, delay: TimeInterval)]
|
||||
|
||||
/// Specifies whether or not the timeline's animations autoreverse.
|
||||
public let autoreverses: Bool
|
||||
|
||||
/// Determines the number of times the timeline's animations will repeat.
|
||||
///
|
||||
/// May be fractional. If the repeatCount is 0, it is ignored.
|
||||
/// Setting this property to greatestFiniteMagnitude will cause the timeline to repeat forever.
|
||||
public let repeatCount: Float
|
||||
|
||||
public var time: TimeInterval {
|
||||
return animations.first?.time ?? 0
|
||||
}
|
||||
|
||||
public var playing: Bool {
|
||||
return animations.first?.playing ?? false
|
||||
}
|
||||
|
||||
public weak var delegate: TimelineDelegate?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public convenience init(view: UIView, animationsByLayer: [CALayer: [CAKeyframeAnimation]], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool = false, repeatCount: Float = 0) {
|
||||
|
||||
let animations = animationsByLayer.map {
|
||||
Animation(layer: $0.0, keyframeAnimations: $0.1, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
|
||||
self.init(view: view, animations: animations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
|
||||
init(view: UIView, animations: [Animation], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool, repeatCount: Float) {
|
||||
self.view = view
|
||||
self.duration = duration
|
||||
self.sounds = sounds
|
||||
self.autoreverses = autoreverses
|
||||
self.repeatCount = repeatCount
|
||||
self.animations = animations
|
||||
self.animations.first?.delegate = self
|
||||
}
|
||||
|
||||
// MARK: - Timeline Playback controls
|
||||
|
||||
/// Reset to the initial state of the timeline
|
||||
public func reset() {
|
||||
for animation in animations {
|
||||
animation.reset()
|
||||
}
|
||||
delegate?.didReset(timeline: self)
|
||||
}
|
||||
|
||||
/// Resume playing the timeline.
|
||||
public func play() {
|
||||
playSounds()
|
||||
for animation in animations {
|
||||
animation.play()
|
||||
}
|
||||
delegate?.didPlay(timeline: self)
|
||||
}
|
||||
|
||||
private func playSounds() {
|
||||
for (sound, delay) in sounds {
|
||||
Sound.playAudio(sound, delay: delay)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pause playing of timeline.
|
||||
public func pause() {
|
||||
for animation in animations {
|
||||
animation.pause()
|
||||
}
|
||||
delegate?.didPause(timeline: self)
|
||||
}
|
||||
|
||||
/// Show timeline at time `time`.
|
||||
public func offset(to time: TimeInterval) {
|
||||
let time = max(min(time, duration), 0)
|
||||
for animation in animations {
|
||||
animation.offset(to: time)
|
||||
}
|
||||
delegate?.didOffset(timeline: self, to: time)
|
||||
}
|
||||
|
||||
/// Returns a reverses version of `self`.
|
||||
var reversed: Timeline {
|
||||
let reversedAnimations = animations.map { $0.reversed }
|
||||
return Timeline(view: view, animations: reversedAnimations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
}
|
||||
|
||||
extension Timeline: AnimationDelegate {
|
||||
func didStop(animation: Animation) {
|
||||
delegate?.didStop(timeline: self)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TimelineDelegate: class {
|
||||
/// Informs the delegate that the timeline `timeline` was reset.
|
||||
func didReset(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` did start playing.
|
||||
func didPlay(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was paused.
|
||||
func didPause(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was offset.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - timeline: The timeline which was offset.
|
||||
/// - time: The time to which `timeline` was offset to.
|
||||
func didOffset(timeline: Timeline, to time: TimeInterval)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was stopped because it completed its active duration.
|
||||
func didStop(timeline: Timeline)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIImage {
|
||||
func resized(to size: CGSize) -> UIImage {
|
||||
let rect = CGRect(origin: .zero, size: size)
|
||||
let vertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
|
||||
return UIGraphicsImageRenderer(size: size).image { _ in
|
||||
let ctx = UIGraphicsGetCurrentContext()
|
||||
ctx?.saveGState()
|
||||
ctx?.concatenate(vertical)
|
||||
draw(in: rect)
|
||||
ctx?.restoreGState()
|
||||
UIGraphicsEndImageContext()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func setTransform(scaleX: CGFloat, scaleY: CGFloat, rotationAngle: CGFloat) {
|
||||
var transform = CGAffineTransform.identity
|
||||
transform = transform.concatenating(CGAffineTransform(scaleX: scaleX, y: 1.0))
|
||||
transform = transform.concatenating(CGAffineTransform(scaleX: 1.0, y: scaleY))
|
||||
transform = transform.concatenating(CGAffineTransform(rotationAngle: rotationAngle))
|
||||
self.transform = transform
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class Animation: NSObject, CAAnimationDelegate {
|
||||
/// Key frame animations which animate the properties of `layer`.
|
||||
fileprivate var keyframeAnimations: [CAKeyframeAnimation]
|
||||
|
||||
/// The CALayer whose properties are animated.
|
||||
open var layer: CALayer
|
||||
|
||||
/// Specifies whether or not the animation should autoreverse.
|
||||
var autoreverses: Bool
|
||||
|
||||
/// Determines the number of times the animation will repeat.
|
||||
///
|
||||
/// May be fractional. If the repeatCount is 0, it is ignored.
|
||||
/// Setting this property to greatestFiniteMagnitude will cause the animation to repeat forever.
|
||||
var repeatCount: Float
|
||||
|
||||
weak var delegate: AnimationDelegate?
|
||||
|
||||
/// The current time of the animation. i.e. what time is being displayed.
|
||||
var time: TimeInterval {
|
||||
return layer.timeOffset
|
||||
}
|
||||
|
||||
/// True if the animation is playing.
|
||||
var playing: Bool {
|
||||
return layer.speed != 0.0
|
||||
}
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public init(layer: CALayer, keyframeAnimations: [CAKeyframeAnimation], autoreverses: Bool = false, repeatCount: Float = 0) {
|
||||
self.layer = layer
|
||||
self.keyframeAnimations = keyframeAnimations
|
||||
self.autoreverses = autoreverses
|
||||
self.repeatCount = repeatCount
|
||||
|
||||
super.init()
|
||||
keyframeAnimations.forEach(configure)
|
||||
reset()
|
||||
}
|
||||
|
||||
private func configure(keyframeAnimation: CAKeyframeAnimation) {
|
||||
keyframeAnimation.delegate = self
|
||||
keyframeAnimation.isRemovedOnCompletion = false
|
||||
keyframeAnimation.fillMode = .both
|
||||
keyframeAnimation.autoreverses = autoreverses
|
||||
keyframeAnimation.repeatCount = repeatCount
|
||||
}
|
||||
|
||||
// MARK: - Playing & Resetting Animation
|
||||
|
||||
/// Resumes the animation from where it was paused.
|
||||
open func play() {
|
||||
let pausedTime = layer.timeOffset
|
||||
layer.speed = 1.0
|
||||
layer.timeOffset = 0.0
|
||||
layer.beginTime = 0.0
|
||||
let timeSincePause = layer.convertTime(CACurrentMediaTime(), from: nil) - pausedTime
|
||||
layer.beginTime = timeSincePause
|
||||
}
|
||||
|
||||
/// Pauses the animation.
|
||||
open func pause() {
|
||||
let pausedTime = layer.convertTime(CACurrentMediaTime(), from: nil)
|
||||
offset(to: pausedTime)
|
||||
}
|
||||
|
||||
/// Resets the animation to time 0.
|
||||
open func reset() {
|
||||
CATransaction.suppressAnimations {
|
||||
layer.removeAllAnimations()
|
||||
layer.beginTime = 0
|
||||
offset(to: 0)
|
||||
|
||||
for keyframeAnimation in keyframeAnimations {
|
||||
layer.setValue(keyframeAnimation.values?.first, forKeyPath: keyframeAnimation.keyPath!)
|
||||
}
|
||||
|
||||
addAllAnimations()
|
||||
layer.speed = 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds all the animations to `layer` so they can be played.
|
||||
private func addAllAnimations() {
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let keyframeAnimations = self?.keyframeAnimations, let layer = self?.layer else {
|
||||
return
|
||||
}
|
||||
for keyframeAnimation in keyframeAnimations {
|
||||
layer.add(keyframeAnimation, forKey: keyframeAnimation.keyPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Driving Animation
|
||||
|
||||
/// Shows the animation at time `time`.
|
||||
open func offset(to time: TimeInterval) {
|
||||
layer.speed = 0.0
|
||||
layer.timeOffset = time
|
||||
}
|
||||
|
||||
// MARK: - CAAnimationDelegate
|
||||
|
||||
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
|
||||
guard flag else {
|
||||
return
|
||||
}
|
||||
|
||||
let time = autoreverses ? 0 : (keyframeAnimations.first?.duration ?? 0)
|
||||
offset(to: time)
|
||||
|
||||
if let keyframeAnimation = anim as? CAKeyframeAnimation,
|
||||
keyframeAnimations.first?.keyPath == keyframeAnimation.keyPath {
|
||||
delegate?.didStop(animation: self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Animation {
|
||||
var reversed: Animation {
|
||||
let reversedKeyFrameAnimations = keyframeAnimations.map { $0.reversed }
|
||||
return Animation(layer: layer, keyframeAnimations: reversedKeyFrameAnimations)
|
||||
}
|
||||
}
|
||||
|
||||
protocol AnimationDelegate: class {
|
||||
func didStop(animation: Animation)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public extension CAKeyframeAnimation {
|
||||
var reversed: CAKeyframeAnimation {
|
||||
let reversed = CAKeyframeAnimation(keyPath: keyPath)
|
||||
reversed.keyTimes = keyTimes?.reversed().map { NSNumber(floatLiteral: 1.0 - $0.doubleValue) }
|
||||
reversed.values = values?.reversed()
|
||||
reversed.timingFunctions = timingFunctions?.reversed().map { $0.reversed }
|
||||
reversed.duration = duration
|
||||
return reversed
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
public extension CAMediaTimingFunction {
|
||||
static let linear = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
|
||||
static let easeIn = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
|
||||
static let easeOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
|
||||
static let easeInEaseOut = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
|
||||
|
||||
var reversed: CAMediaTimingFunction {
|
||||
let (c1, c2) = controlPoints
|
||||
let x1 = Float(1 - c2.x)
|
||||
let y1 = Float(1 - c2.y)
|
||||
let x2 = Float(1 - c1.x)
|
||||
let y2 = Float(1 - c1.y)
|
||||
return CAMediaTimingFunction(controlPoints: x1, y1, x2, y2)
|
||||
}
|
||||
|
||||
var controlPoints: (CGPoint, CGPoint) {
|
||||
var c1: [Float] = [0, 0]
|
||||
var c2: [Float] = [0, 0]
|
||||
getControlPoint(at: 1, values: &c1)
|
||||
getControlPoint(at: 2, values: &c2)
|
||||
|
||||
let c1x = CGFloat(c1[0])
|
||||
let c1y = CGFloat(c1[1])
|
||||
let c2x = CGFloat(c2[0])
|
||||
let c2y = CGFloat(c2[1])
|
||||
|
||||
return (CGPoint(x: c1x, y: c1y), CGPoint(x: c2x, y: c2y))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension CATransaction {
|
||||
static public func suppressAnimations(actions: () -> Void) {
|
||||
begin()
|
||||
setAnimationDuration(0)
|
||||
actions()
|
||||
commit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,310 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import QuartzCore
|
||||
|
||||
public func CGPathCreateWithSVGString(_ string: String) -> CGPath? {
|
||||
let parser = SVGPathStringParser()
|
||||
return try? parser.parse(string)
|
||||
}
|
||||
|
||||
class SVGPathStringParser {
|
||||
enum Error: Swift.Error {
|
||||
case invalidSyntax
|
||||
case missingValues
|
||||
}
|
||||
|
||||
let path = CGMutablePath()
|
||||
var currentPoint = CGPoint()
|
||||
var lastControlPoint = CGPoint()
|
||||
|
||||
var command: UnicodeScalar?
|
||||
var values = [CGFloat]()
|
||||
|
||||
func parse(_ string: String) throws -> CGPath {
|
||||
let tokens = SVGPathStringTokenizer(string: string).tokenize()
|
||||
|
||||
for token in tokens {
|
||||
switch token {
|
||||
case .command(let c):
|
||||
command = c
|
||||
values.removeAll()
|
||||
|
||||
case .value(let v):
|
||||
values.append(v)
|
||||
}
|
||||
|
||||
do {
|
||||
try addCommand()
|
||||
} catch Error.missingValues {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
fileprivate func addCommand() throws {
|
||||
guard let command = command else {
|
||||
return
|
||||
}
|
||||
|
||||
switch command {
|
||||
case "M":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
path.move(to: CGPoint(x: values[0], y: values[1]))
|
||||
currentPoint = CGPoint(x: values[0], y: values[1])
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "m":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.move(to: point)
|
||||
currentPoint.x += values[0]
|
||||
currentPoint.y += values[1]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "L":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: values[0], y: values[1])
|
||||
path.addLine(to: point)
|
||||
currentPoint = CGPoint(x: values[0], y: values[1])
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "l":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.addLine(to: point)
|
||||
currentPoint.x += values[0]
|
||||
currentPoint.y += values[1]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(2)
|
||||
|
||||
case "H":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: values[0], y: currentPoint.y)
|
||||
path.addLine(to: point)
|
||||
currentPoint.x = values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "h":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y)
|
||||
path.addLine(to: point)
|
||||
currentPoint.x += values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "V":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x, y: values[0])
|
||||
path.addLine(to: point)
|
||||
currentPoint.y = values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "v":
|
||||
if values.count < 1 { throw Error.missingValues }
|
||||
let point = CGPoint(x: currentPoint.x, y: currentPoint.y + values[0])
|
||||
path.addLine(to: point)
|
||||
currentPoint.y += values[0]
|
||||
lastControlPoint = currentPoint
|
||||
values.removeFirst(1)
|
||||
|
||||
case "C":
|
||||
if values.count < 6 { throw Error.missingValues }
|
||||
let c1 = CGPoint(x: values[0], y: values[1])
|
||||
let c2 = CGPoint(x: values[2], y: values[3])
|
||||
let to = CGPoint(x: values[4], y: values[5])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(6)
|
||||
|
||||
case "c":
|
||||
if values.count < 6 { throw Error.missingValues }
|
||||
let c1 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let c2 = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
let to = CGPoint(x: currentPoint.x + values[4], y: currentPoint.y + values[5])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(6)
|
||||
|
||||
case "S":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
var c1 = CGPoint()
|
||||
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let c2 = CGPoint(x: values[0], y: values[1])
|
||||
let to = CGPoint(x: values[2], y: values[3])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "s":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
var c1 = CGPoint()
|
||||
c1.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
c1.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let c2 = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
path.addCurve(to: to, control1: c1, control2: c2)
|
||||
lastControlPoint = c2
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "Q":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
let control = CGPoint(x: values[0], y: values[1])
|
||||
let to = CGPoint(x: values[2], y: values[3])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "q":
|
||||
if values.count < 4 { throw Error.missingValues }
|
||||
let control = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
let to = CGPoint(x: currentPoint.x + values[2], y: currentPoint.y + values[3])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(4)
|
||||
|
||||
case "T":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
var control = CGPoint()
|
||||
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let to = CGPoint(x: values[0], y: values[1])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(2)
|
||||
|
||||
case "t":
|
||||
if values.count < 2 { throw Error.missingValues }
|
||||
var control = CGPoint()
|
||||
control.x = currentPoint.x + (currentPoint.x - lastControlPoint.x)
|
||||
control.y = currentPoint.y + (currentPoint.y - lastControlPoint.y)
|
||||
let to = CGPoint(x: currentPoint.x + values[0], y: currentPoint.y + values[1])
|
||||
path.addQuadCurve(to: to, control: control)
|
||||
lastControlPoint = control
|
||||
currentPoint = to
|
||||
values.removeFirst(2)
|
||||
|
||||
case "Z", "z":
|
||||
path.closeSubpath()
|
||||
self.command = nil
|
||||
|
||||
default:
|
||||
throw Error.invalidSyntax
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SVGPathStringTokenizer {
|
||||
enum Token {
|
||||
case command(UnicodeScalar)
|
||||
case value(CGFloat)
|
||||
}
|
||||
|
||||
let separatorCharacterSet = CharacterSet(charactersIn: " \t\r\n,")
|
||||
let commandCharacterSet = CharacterSet(charactersIn: "mMLlHhVvzZCcSsQqTt")
|
||||
let numberStartCharacterSet = CharacterSet(charactersIn: "-+.0123456789")
|
||||
let numberCharacterSet = CharacterSet(charactersIn: ".0123456789eE")
|
||||
|
||||
var string: String
|
||||
var range: Range<String.UnicodeScalarView.Index>
|
||||
|
||||
init(string: String) {
|
||||
self.string = string
|
||||
range = string.unicodeScalars.startIndex..<string.unicodeScalars.endIndex
|
||||
}
|
||||
|
||||
func tokenize() -> [Token] {
|
||||
var array = [Token]()
|
||||
while let token = nextToken() {
|
||||
array.append(token)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
func nextToken() -> Token? {
|
||||
skipSeparators()
|
||||
|
||||
guard let c = get() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if commandCharacterSet.isMember(c) {
|
||||
return .command(c)
|
||||
}
|
||||
|
||||
if numberStartCharacterSet.isMember(c) {
|
||||
var numberString = String(c)
|
||||
while let c = peek(), numberCharacterSet.isMember(c) {
|
||||
numberString += String(c)
|
||||
get()
|
||||
}
|
||||
|
||||
if let value = Double(numberString) {
|
||||
return .value(CGFloat(value))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func get() -> UnicodeScalar? {
|
||||
guard range.lowerBound != range.upperBound else {
|
||||
return nil
|
||||
}
|
||||
let c = string.unicodeScalars[range.lowerBound]
|
||||
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
|
||||
return c
|
||||
}
|
||||
|
||||
func peek() -> UnicodeScalar? {
|
||||
guard range.lowerBound != range.upperBound else {
|
||||
return nil
|
||||
}
|
||||
return string.unicodeScalars[range.lowerBound]
|
||||
}
|
||||
|
||||
func skipSeparators() {
|
||||
while range.lowerBound != range.upperBound && separatorCharacterSet.isMember(string.unicodeScalars[range.lowerBound]) {
|
||||
range = string.unicodeScalars.index(after: range.lowerBound)..<range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension CharacterSet {
|
||||
public func isMember(_ c: UnicodeScalar) -> Bool {
|
||||
return contains(UnicodeScalar(c.value)!)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension NSMutableParagraphStyle {
|
||||
convenience init(alignment: NSTextAlignment,
|
||||
firstLineHeadIndent: CGFloat,
|
||||
headIndent: CGFloat,
|
||||
tailIndent: CGFloat,
|
||||
lineHeightMultiple: CGFloat,
|
||||
maximumLineHeight: CGFloat,
|
||||
minimumLineHeight: CGFloat,
|
||||
lineSpacing: CGFloat,
|
||||
paragraphSpacing: CGFloat,
|
||||
paragraphSpacingBefore: CGFloat) {
|
||||
self.init()
|
||||
self.alignment = alignment
|
||||
self.firstLineHeadIndent = firstLineHeadIndent
|
||||
self.headIndent = headIndent
|
||||
self.tailIndent = tailIndent
|
||||
self.lineHeightMultiple = lineHeightMultiple
|
||||
self.maximumLineHeight = maximumLineHeight
|
||||
self.minimumLineHeight = minimumLineHeight
|
||||
self.lineSpacing = lineSpacing
|
||||
self.paragraphSpacing = paragraphSpacing
|
||||
self.paragraphSpacingBefore = paragraphSpacingBefore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension NSShadow {
|
||||
convenience init(blurRadius: CGFloat, offset: CGSize, color: UIColor) {
|
||||
self.init()
|
||||
shadowBlurRadius = blurRadius
|
||||
shadowOffset = offset
|
||||
shadowColor = color
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright © 2016-2019 JABT
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class ShapeView: UIView {
|
||||
open var shapeLayer: CAShapeLayer {
|
||||
return layer as! CAShapeLayer // swiftlint:disable:this force_cast
|
||||
}
|
||||
|
||||
/// A sublayer which can be used to apply a gradient fill to `self`.
|
||||
open var gradientLayer: CAGradientLayer? {
|
||||
set {
|
||||
// Remove old gradient layer
|
||||
if let gradientLayer = gradientLayer {
|
||||
gradientLayer.removeFromSuperlayer()
|
||||
}
|
||||
// Replace old gradient with new one
|
||||
if let newGradientLayer = newValue {
|
||||
layer.addSublayer(newGradientLayer)
|
||||
}
|
||||
}
|
||||
|
||||
get {
|
||||
return layer.sublayers?.first(where: { $0 is CAGradientLayer }) as? CAGradientLayer
|
||||
}
|
||||
}
|
||||
|
||||
public func addGradient(type: CAGradientLayerType, startPoint: CGPoint, endPoint: CGPoint, stops: [(color: CGColor, location: NSNumber)]) {
|
||||
let gradientLayer = CAGradientLayer()
|
||||
gradientLayer.frame = shapeLayer.bounds
|
||||
self.gradientLayer = gradientLayer
|
||||
|
||||
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = shapeLayer.path
|
||||
mask.fillColor = UIColor.black.cgColor
|
||||
mask.strokeColor = nil
|
||||
|
||||
gradientLayer.startPoint = startPoint
|
||||
gradientLayer.endPoint = endPoint
|
||||
gradientLayer.colors = stops.map { $0.color }
|
||||
gradientLayer.locations = stops.map { $0.location }
|
||||
gradientLayer.type = type
|
||||
gradientLayer.frame = shapeLayer.bounds
|
||||
gradientLayer.mask = mask
|
||||
}
|
||||
|
||||
open var path: CGPath? {
|
||||
get {
|
||||
return shapeLayer.path
|
||||
}
|
||||
set {
|
||||
shapeLayer.path = newValue
|
||||
}
|
||||
}
|
||||
|
||||
override open class var layerClass: AnyClass {
|
||||
return CAShapeLayer.self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import AVFoundation
|
||||
|
||||
public final class Sound {
|
||||
|
||||
static func playAudio(_ audio: AVAudioPlayer, delay: TimeInterval) {
|
||||
audio.prepareToPlay()
|
||||
let time = DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
|
||||
DispatchQueue.main.asyncAfter(deadline: time) {
|
||||
audio.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016-2019 JABT
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
open class TextView: UILabel {
|
||||
open var textLayer: CATextLayer {
|
||||
return layer as! CATextLayer // swiftlint:disable:this force_cast
|
||||
}
|
||||
|
||||
override open class var layerClass: AnyClass {
|
||||
return CATextLayer.self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
|
||||
open class Timeline {
|
||||
public var view: UIView
|
||||
public var duration: TimeInterval
|
||||
public let animations: [Animation]
|
||||
public let sounds: [(sound: AVAudioPlayer, delay: TimeInterval)]
|
||||
|
||||
/// Specifies whether or not the timeline's animations autoreverse.
|
||||
public let autoreverses: Bool
|
||||
|
||||
/// Determines the number of times the timeline's animations will repeat.
|
||||
///
|
||||
/// May be fractional. If the repeatCount is 0, it is ignored.
|
||||
/// Setting this property to greatestFiniteMagnitude will cause the timeline to repeat forever.
|
||||
public let repeatCount: Float
|
||||
|
||||
public var time: TimeInterval {
|
||||
return animations.first?.time ?? 0
|
||||
}
|
||||
|
||||
public var playing: Bool {
|
||||
return animations.first?.playing ?? false
|
||||
}
|
||||
|
||||
public weak var delegate: TimelineDelegate?
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
public convenience init(view: UIView, animationsByLayer: [CALayer: [CAKeyframeAnimation]], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool = false, repeatCount: Float = 0) {
|
||||
|
||||
let animations = animationsByLayer.map {
|
||||
Animation(layer: $0.0, keyframeAnimations: $0.1, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
|
||||
self.init(view: view, animations: animations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
|
||||
init(view: UIView, animations: [Animation], sounds: [(sound: AVAudioPlayer, delay: TimeInterval)], duration: TimeInterval, autoreverses: Bool, repeatCount: Float) {
|
||||
self.view = view
|
||||
self.duration = duration
|
||||
self.sounds = sounds
|
||||
self.autoreverses = autoreverses
|
||||
self.repeatCount = repeatCount
|
||||
self.animations = animations
|
||||
self.animations.first?.delegate = self
|
||||
}
|
||||
|
||||
// MARK: - Timeline Playback controls
|
||||
|
||||
/// Reset to the initial state of the timeline
|
||||
public func reset() {
|
||||
for animation in animations {
|
||||
animation.reset()
|
||||
}
|
||||
delegate?.didReset(timeline: self)
|
||||
}
|
||||
|
||||
/// Resume playing the timeline.
|
||||
public func play() {
|
||||
playSounds()
|
||||
for animation in animations {
|
||||
animation.play()
|
||||
}
|
||||
delegate?.didPlay(timeline: self)
|
||||
}
|
||||
|
||||
private func playSounds() {
|
||||
for (sound, delay) in sounds {
|
||||
Sound.playAudio(sound, delay: delay)
|
||||
}
|
||||
}
|
||||
|
||||
/// Pause playing of timeline.
|
||||
public func pause() {
|
||||
for animation in animations {
|
||||
animation.pause()
|
||||
}
|
||||
delegate?.didPause(timeline: self)
|
||||
}
|
||||
|
||||
/// Show timeline at time `time`.
|
||||
public func offset(to time: TimeInterval) {
|
||||
let time = max(min(time, duration), 0)
|
||||
for animation in animations {
|
||||
animation.offset(to: time)
|
||||
}
|
||||
delegate?.didOffset(timeline: self, to: time)
|
||||
}
|
||||
|
||||
/// Returns a reverses version of `self`.
|
||||
var reversed: Timeline {
|
||||
let reversedAnimations = animations.map { $0.reversed }
|
||||
return Timeline(view: view, animations: reversedAnimations, sounds: sounds, duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
}
|
||||
|
||||
extension Timeline: AnimationDelegate {
|
||||
func didStop(animation: Animation) {
|
||||
delegate?.didStop(timeline: self)
|
||||
}
|
||||
}
|
||||
|
||||
public protocol TimelineDelegate: class {
|
||||
/// Informs the delegate that the timeline `timeline` was reset.
|
||||
func didReset(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` did start playing.
|
||||
func didPlay(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was paused.
|
||||
func didPause(timeline: Timeline)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was offset.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - timeline: The timeline which was offset.
|
||||
/// - time: The time to which `timeline` was offset to.
|
||||
func didOffset(timeline: Timeline, to time: TimeInterval)
|
||||
|
||||
/// Informs the delegate that the timeline `timeline` was stopped because it completed its active duration.
|
||||
func didStop(timeline: Timeline)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIImage {
|
||||
func resized(to size: CGSize) -> UIImage {
|
||||
let rect = CGRect(origin: .zero, size: size)
|
||||
let vertical = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
|
||||
return UIGraphicsImageRenderer(size: size).image { _ in
|
||||
let ctx = UIGraphicsGetCurrentContext()
|
||||
ctx?.saveGState()
|
||||
ctx?.concatenate(vertical)
|
||||
draw(in: rect)
|
||||
ctx?.restoreGState()
|
||||
UIGraphicsEndImageContext()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016-2019 JABT Labs Inc.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions: The above copyright
|
||||
// notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIView {
|
||||
func setTransform(scaleX: CGFloat, scaleY: CGFloat, rotationAngle: CGFloat) {
|
||||
var transform = CGAffineTransform.identity
|
||||
transform = transform.concatenating(CGAffineTransform(scaleX: scaleX, y: 1.0))
|
||||
transform = transform.concatenating(CGAffineTransform(scaleX: 1.0, y: scaleY))
|
||||
transform = transform.concatenating(CGAffineTransform(rotationAngle: rotationAngle))
|
||||
self.transform = transform
|
||||
}
|
||||
}
|
||||
@@ -843,134 +843,147 @@
|
||||
|
||||
#pragma mark - Swipe actions
|
||||
|
||||
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
NSMutableArray* actions = [[NSMutableArray alloc] init];
|
||||
MXRoom* room = [self.dataSource getRoomAtIndexPath:indexPath];
|
||||
|
||||
if (room)
|
||||
{
|
||||
// Display no action for the invited room
|
||||
if (room.summary.membership == MXMembershipInvite)
|
||||
{
|
||||
return actions;
|
||||
}
|
||||
|
||||
// Store the identifier of the room related to the edited cell.
|
||||
editedRoomId = room.roomId;
|
||||
|
||||
NSString* title = @" ";
|
||||
|
||||
// Direct chat toggle
|
||||
BOOL isDirect = room.isDirect;
|
||||
|
||||
UITableViewRowAction *directAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self makeDirectEditedRoom:!isDirect];
|
||||
|
||||
}];
|
||||
|
||||
UIImage *actionIcon = isDirect ? [UIImage imageNamed:@"directChatOff"] : [UIImage imageNamed:@"directChatOn"];
|
||||
directAction.backgroundColor = [MXKTools convertImageToPatternColor:isDirect ? @"directChatOn" : @"directChatOff" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:directAction atIndex:0];
|
||||
|
||||
|
||||
// Notification toggle
|
||||
BOOL isMuted = room.isMute || room.isMentionsOnly;
|
||||
|
||||
UITableViewRowAction *muteAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self muteEditedRoomNotifications:!isMuted];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = isMuted ? [UIImage imageNamed:@"notifications"] : [UIImage imageNamed:@"notificationsOff"];
|
||||
muteAction.backgroundColor = [MXKTools convertImageToPatternColor:isMuted ? @"notifications" : @"notificationsOff" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:muteAction atIndex:0];
|
||||
|
||||
// Favorites management
|
||||
MXRoomTag* currentTag = nil;
|
||||
|
||||
// Get the room tag (use only the first one).
|
||||
if (room.accountData.tags)
|
||||
{
|
||||
NSArray<MXRoomTag*>* tags = room.accountData.tags.allValues;
|
||||
if (tags.count)
|
||||
{
|
||||
currentTag = tags[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTag && [kMXRoomTagFavourite isEqualToString:currentTag.name])
|
||||
{
|
||||
UITableViewRowAction* action = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self updateEditedRoomTag:nil];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = [UIImage imageNamed:@"favourite"];
|
||||
action.backgroundColor = [MXKTools convertImageToPatternColor:@"favourite" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:action atIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
UITableViewRowAction* action = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self updateEditedRoomTag:kMXRoomTagFavourite];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = [UIImage imageNamed:@"favouriteOff"];
|
||||
action.backgroundColor = [MXKTools convertImageToPatternColor:@"favouriteOff" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:action atIndex:0];
|
||||
}
|
||||
|
||||
if (currentTag && [kMXRoomTagLowPriority isEqualToString:currentTag.name])
|
||||
{
|
||||
UITableViewRowAction* action = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self updateEditedRoomTag:nil];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = [UIImage imageNamed:@"priorityHigh"];
|
||||
action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityHigh" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:action atIndex:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
UITableViewRowAction* action = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleNormal title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self updateEditedRoomTag:kMXRoomTagLowPriority];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = [UIImage imageNamed:@"priorityLow"];
|
||||
action.backgroundColor = [MXKTools convertImageToPatternColor:@"priorityLow" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
[actions insertObject:action atIndex:0];
|
||||
}
|
||||
|
||||
UITableViewRowAction *leaveAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDestructive title:title handler:^(UITableViewRowAction *action, NSIndexPath *indexPath){
|
||||
|
||||
[self leaveEditedRoom];
|
||||
|
||||
}];
|
||||
|
||||
actionIcon = [UIImage imageNamed:@"leave"];
|
||||
leaveAction.backgroundColor = [MXKTools convertImageToPatternColor:@"leave" backgroundColor:ThemeService.shared.theme.headerBackgroundColor patternSize:CGSizeMake(74, 74) resourceSize:actionIcon.size];
|
||||
|
||||
[actions insertObject:leaveAction atIndex:0];
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[self cancelEditionMode:isRefreshPending];
|
||||
}
|
||||
|
||||
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return UITableViewCellEditingStyleNone;
|
||||
}
|
||||
|
||||
- (nullable UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
MXRoom *room = [self.dataSource getRoomAtIndexPath:indexPath];
|
||||
|
||||
if (!room)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Display no action for the invited room
|
||||
if (room.summary.membership == MXMembershipInvite)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Store the identifier of the room related to the edited cell.
|
||||
editedRoomId = room.roomId;
|
||||
|
||||
UIColor *selectedColor = ThemeService.shared.theme.tintColor;
|
||||
UIColor *unselectedColor = ThemeService.shared.theme.tabBarUnselectedItemTintColor;
|
||||
UIColor *actionBackgroundColor = ThemeService.shared.theme.baseColor;
|
||||
|
||||
NSString* title = @" ";
|
||||
|
||||
// Direct chat toggle
|
||||
|
||||
BOOL isDirect = room.isDirect;
|
||||
|
||||
UIContextualAction *directChatAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
|
||||
title:title
|
||||
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
|
||||
[self makeDirectEditedRoom:!isDirect];
|
||||
completionHandler(YES);
|
||||
}];
|
||||
directChatAction.backgroundColor = actionBackgroundColor;
|
||||
|
||||
UIImage *directChatImage = [UIImage imageNamed:@"room_action_direct_chat"];
|
||||
directChatImage = [directChatImage vc_tintedImageUsingColor:isDirect ? selectedColor : unselectedColor];
|
||||
directChatAction.image = directChatImage;
|
||||
|
||||
// Notification toggle
|
||||
|
||||
BOOL isMuted = room.isMute || room.isMentionsOnly;
|
||||
|
||||
UIContextualAction *muteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
|
||||
title:title
|
||||
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
|
||||
[self muteEditedRoomNotifications:!isMuted];
|
||||
completionHandler(YES);
|
||||
}];
|
||||
muteAction.backgroundColor = actionBackgroundColor;
|
||||
|
||||
UIImage *notificationImage = [UIImage imageNamed:@"room_action_notification"];
|
||||
notificationImage = [notificationImage vc_tintedImageUsingColor:isMuted ? unselectedColor : selectedColor];
|
||||
muteAction.image = notificationImage;
|
||||
|
||||
// Favorites management
|
||||
|
||||
MXRoomTag* currentTag = nil;
|
||||
|
||||
// Get the room tag (use only the first one).
|
||||
if (room.accountData.tags)
|
||||
{
|
||||
NSArray<MXRoomTag*>* tags = room.accountData.tags.allValues;
|
||||
if (tags.count)
|
||||
{
|
||||
currentTag = tags[0];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isFavourite = (currentTag && [kMXRoomTagFavourite isEqualToString:currentTag.name]);
|
||||
|
||||
UIContextualAction *favouriteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
|
||||
title:title
|
||||
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
|
||||
NSString *favouriteTag = isFavourite ? nil : kMXRoomTagFavourite;
|
||||
[self updateEditedRoomTag:favouriteTag];
|
||||
completionHandler(YES);
|
||||
}];
|
||||
favouriteAction.backgroundColor = actionBackgroundColor;
|
||||
|
||||
UIImage *favouriteImage = [UIImage imageNamed:@"room_action_favourite"];
|
||||
favouriteImage = [favouriteImage vc_tintedImageUsingColor:isFavourite ? selectedColor : unselectedColor];
|
||||
favouriteAction.image = favouriteImage;
|
||||
|
||||
// Priority toggle
|
||||
|
||||
BOOL isInLowPriority = (currentTag && [kMXRoomTagLowPriority isEqualToString:currentTag.name]);
|
||||
|
||||
UIContextualAction *priorityAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
|
||||
title:title
|
||||
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
|
||||
NSString *priorityTag = isInLowPriority ? nil : kMXRoomTagLowPriority;
|
||||
[self updateEditedRoomTag:priorityTag];
|
||||
completionHandler(YES);
|
||||
}];
|
||||
priorityAction.backgroundColor = actionBackgroundColor;
|
||||
|
||||
UIImage *priorityImage = isInLowPriority ? [UIImage imageNamed:@"room_action_priority_high"] : [UIImage imageNamed:@"room_action_priority_low"];
|
||||
priorityImage = [priorityImage vc_tintedImageUsingColor:unselectedColor];
|
||||
priorityAction.image = priorityImage;
|
||||
|
||||
// Leave action
|
||||
|
||||
UIContextualAction *leaveAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
|
||||
title:title
|
||||
handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
|
||||
[self leaveEditedRoom];
|
||||
completionHandler(YES);
|
||||
}];
|
||||
leaveAction.backgroundColor = actionBackgroundColor;
|
||||
|
||||
UIImage *leaveImage = [UIImage imageNamed:@"room_action_leave"];
|
||||
leaveImage = [leaveImage vc_tintedImageUsingColor:unselectedColor];
|
||||
leaveAction.image = leaveImage;
|
||||
|
||||
// Create swipe action configuration
|
||||
|
||||
NSArray<UIContextualAction*> *actions = @[
|
||||
leaveAction,
|
||||
priorityAction,
|
||||
favouriteAction,
|
||||
muteAction,
|
||||
directChatAction
|
||||
];
|
||||
|
||||
UISwipeActionsConfiguration *swipeActionConfiguration = [UISwipeActionsConfiguration configurationWithActions:actions];
|
||||
swipeActionConfiguration.performsFirstActionWithFullSwipe = NO;
|
||||
return swipeActionConfiguration;
|
||||
}
|
||||
|
||||
- (void)leaveEditedRoom
|
||||
{
|
||||
if (editedRoomId)
|
||||
@@ -1584,7 +1597,7 @@
|
||||
|
||||
plusButtonImageView.backgroundColor = [UIColor clearColor];
|
||||
plusButtonImageView.contentMode = UIViewContentModeCenter;
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"create_room"];
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"plus_floating_action"];
|
||||
plusButtonImageView.layer.shadowOpacity = 0.3;
|
||||
plusButtonImageView.layer.shadowOffset = CGSizeMake(0, 3);
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@
|
||||
}];
|
||||
|
||||
[AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_groups", @"Vector", nil);
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorBlue;
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
@@ -559,7 +559,7 @@
|
||||
|
||||
plusButtonImageView.backgroundColor = [UIColor clearColor];
|
||||
plusButtonImageView.contentMode = UIViewContentModeCenter;
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"create_group"];
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"plus_floating_action"];
|
||||
plusButtonImageView.layer.shadowOpacity = 0.3;
|
||||
plusButtonImageView.layer.shadowOffset = CGSizeMake(0, 3);
|
||||
|
||||
|
||||
@@ -132,16 +132,16 @@
|
||||
|
||||
[self.leftButton.layer setCornerRadius:5];
|
||||
self.leftButton.clipsToBounds = YES;
|
||||
self.leftButton.backgroundColor = ThemeService.shared.riotColorBlue;
|
||||
self.leftButton.backgroundColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[self.rightButton.layer setCornerRadius:5];
|
||||
self.rightButton.clipsToBounds = YES;
|
||||
self.rightButton.backgroundColor = ThemeService.shared.riotColorBlue;
|
||||
self.rightButton.backgroundColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
if (_groupLongDescription)
|
||||
{
|
||||
_groupLongDescription.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
_groupLongDescription.tintColor = ThemeService.shared.riotColorBlue;
|
||||
_groupLongDescription.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// Update HTML loading options
|
||||
NSUInteger bgColor = [MXKTools rgbValueWithColor:ThemeService.shared.theme.headerBackgroundColor];
|
||||
|
||||
@@ -538,7 +538,7 @@
|
||||
contactsDataSource.displaySearchInputInContactsList = YES;
|
||||
contactsDataSource.forceMatrixIdInDisplayName = YES;
|
||||
// Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user.
|
||||
contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
|
||||
contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor];
|
||||
|
||||
// List all the participants matrix user id to ignore them during the contacts search.
|
||||
for (Contact *contact in actualParticipants)
|
||||
@@ -1204,8 +1204,8 @@
|
||||
- (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar
|
||||
{
|
||||
// bar tint color
|
||||
searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
||||
searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
||||
searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
searchBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
||||
|
||||
@@ -1216,7 +1216,7 @@
|
||||
// Magnifying glass icon.
|
||||
UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView;
|
||||
leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
leftImageView.tintColor = ThemeService.shared.riotColorBlue;
|
||||
leftImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// remove the gray background color
|
||||
UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"];
|
||||
@@ -1225,10 +1225,7 @@
|
||||
effectBackgroundBottom.hidden = YES;
|
||||
|
||||
// place holder
|
||||
searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder
|
||||
attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
||||
NSUnderlineColorAttributeName: ThemeService.shared.riotColorBlue,
|
||||
NSForegroundColorAttributeName: ThemeService.shared.riotColorBlue}];
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.placeholderTextColor;
|
||||
}
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
|
||||
@@ -602,8 +602,8 @@
|
||||
- (void)refreshSearchBarItemsColor:(UISearchBar *)searchBar
|
||||
{
|
||||
// bar tint color
|
||||
searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
||||
searchBar.tintColor = ThemeService.shared.riotColorBlue;
|
||||
searchBar.barTintColor = searchBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
searchBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// FIXME: this all seems incredibly fragile and tied to gutwrenching the current UISearchBar internals.
|
||||
|
||||
@@ -614,7 +614,7 @@
|
||||
// Magnifying glass icon.
|
||||
UIImageView *leftImageView = (UIImageView *)searchBarTextField.leftView;
|
||||
leftImageView.image = [leftImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
|
||||
leftImageView.tintColor = ThemeService.shared.riotColorBlue;
|
||||
leftImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// remove the gray background color
|
||||
UIView *effectBackgroundTop = [searchBarTextField valueForKey:@"_effectBackgroundTop"];
|
||||
@@ -623,10 +623,7 @@
|
||||
effectBackgroundBottom.hidden = YES;
|
||||
|
||||
// place holder
|
||||
searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder
|
||||
attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
||||
NSUnderlineColorAttributeName: ThemeService.shared.riotColorBlue,
|
||||
NSForegroundColorAttributeName: ThemeService.shared.riotColorBlue}];
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.placeholderTextColor;
|
||||
}
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
self.enableBarTintColorStatusChange = NO;
|
||||
self.rageShakeManager = [RageShakeManager sharedManager];
|
||||
|
||||
self.sectionHeaderTintColor = ThemeService.shared.riotColorBlue;
|
||||
self.sectionHeaderTintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// Keep visible the status bar by default.
|
||||
isStatusBarHidden = NO;
|
||||
|
||||
@@ -55,8 +55,8 @@ NSString *const kGroupInviteTableViewCellRoomKey = @"kGroupInviteTableViewCellRo
|
||||
{
|
||||
[super customizeTableViewCellRendering];
|
||||
|
||||
self.leftButton.backgroundColor = ThemeService.shared.riotColorBlue;
|
||||
self.rightButton.backgroundColor = ThemeService.shared.riotColorBlue;
|
||||
self.leftButton.backgroundColor = ThemeService.shared.theme.tintColor;
|
||||
self.rightButton.backgroundColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
self.noticeBadgeView.backgroundColor = ThemeService.shared.theme.noticeColor;
|
||||
}
|
||||
|
||||
@@ -862,6 +862,7 @@
|
||||
|
||||
// Set the right value of the tick box
|
||||
localContactsCheckbox.image = hideNonMatrixEnabledContacts ? [UIImage imageNamed:@"selection_tick"] : [UIImage imageNamed:@"selection_untick"];
|
||||
localContactsCheckbox.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
// Add the check box container
|
||||
[sectionHeader addSubview:localContactsCheckboxContainer];
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "RecentsDataSource.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface FavouritesViewController ()
|
||||
{
|
||||
@@ -55,7 +56,7 @@
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_favourites", @"Vector", nil);
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorIndigo;
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
if (recentsDataSource)
|
||||
{
|
||||
|
||||
@@ -320,6 +320,9 @@
|
||||
|
||||
if (editedRoomId)
|
||||
{
|
||||
UIColor *selectedColor = ThemeService.shared.theme.tintColor;
|
||||
UIColor *unselectedColor = ThemeService.shared.theme.tabBarUnselectedItemTintColor;
|
||||
|
||||
// Disable collection scrolling during edition
|
||||
tableViewCell.collectionView.scrollEnabled = NO;
|
||||
|
||||
@@ -335,11 +338,13 @@
|
||||
// Update the edition menu content (Use the button tag to store the current value).
|
||||
tableViewCell.directChatButton.tag = room.isDirect;
|
||||
[tableViewCell.directChatButton addTarget:self action:@selector(onDirectChatButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
tableViewCell.directChatImageView.image = tableViewCell.directChatButton.tag ? [UIImage imageNamed:@"directChatOff"] : [UIImage imageNamed:@"directChatOn"];
|
||||
tableViewCell.directChatImageView.image = [UIImage imageNamed:@"room_action_direct_chat"];
|
||||
tableViewCell.directChatImageView.tintColor = room.isDirect ? selectedColor : unselectedColor;
|
||||
|
||||
tableViewCell.notificationsButton.tag = room.isMute || room.isMentionsOnly;
|
||||
[tableViewCell.notificationsButton addTarget:self action:@selector(onNotificationsButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
tableViewCell.notificationsImageView.image = tableViewCell.notificationsButton.tag ? [UIImage imageNamed:@"notifications"] : [UIImage imageNamed:@"notificationsOff"];
|
||||
tableViewCell.notificationsImageView.image = [UIImage imageNamed:@"room_action_notification"];
|
||||
tableViewCell.notificationsImageView.tintColor = tableViewCell.notificationsButton.tag ? unselectedColor : selectedColor;
|
||||
|
||||
// Get the room tag (use only the first one).
|
||||
MXRoomTag* currentTag = nil;
|
||||
@@ -354,14 +359,17 @@
|
||||
|
||||
tableViewCell.favouriteButton.tag = (currentTag && [kMXRoomTagFavourite isEqualToString:currentTag.name]);
|
||||
[tableViewCell.favouriteButton addTarget:self action:@selector(onFavouriteButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
tableViewCell.favouriteImageView.image = tableViewCell.favouriteButton.tag ? [UIImage imageNamed:@"favouriteOff"] : [UIImage imageNamed:@"favourite"];
|
||||
tableViewCell.favouriteImageView.image = [UIImage imageNamed:@"room_action_favourite"];
|
||||
tableViewCell.favouriteImageView.tintColor = tableViewCell.favouriteButton.tag ? selectedColor : unselectedColor;
|
||||
|
||||
tableViewCell.priorityButton.tag = (currentTag && [kMXRoomTagLowPriority isEqualToString:currentTag.name]);
|
||||
[tableViewCell.priorityButton addTarget:self action:@selector(onPriorityButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
tableViewCell.priorityImageView.image = tableViewCell.priorityButton.tag ? [UIImage imageNamed:@"priorityHigh"] : [UIImage imageNamed:@"priorityLow"];
|
||||
tableViewCell.priorityImageView.image = tableViewCell.priorityButton.tag ? [UIImage imageNamed:@"room_action_priority_high"] : [UIImage imageNamed:@"room_action_priority_low"];
|
||||
tableViewCell.priorityImageView.tintColor = unselectedColor;
|
||||
|
||||
[tableViewCell.leaveButton addTarget:self action:@selector(onLeaveButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
tableViewCell.leaveImageView.image = [UIImage imageNamed:@"leave"];
|
||||
tableViewCell.leaveImageView.image = [UIImage imageNamed:@"room_action_leave"];
|
||||
tableViewCell.leaveImageView.tintColor = unselectedColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#import "ThemeService.h"
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
static CGFloat const kEditionViewCornerRadius = 10.0;
|
||||
|
||||
@implementation TableViewCellWithCollectionView
|
||||
|
||||
- (void)awakeFromNib
|
||||
@@ -26,6 +28,8 @@
|
||||
|
||||
self.editionViewHeightConstraint.constant = 0;
|
||||
self.editionViewBottomConstraint.constant = 0;
|
||||
|
||||
self.editionView.layer.masksToBounds = YES;
|
||||
}
|
||||
|
||||
- (void)customizeTableViewCellRendering
|
||||
@@ -50,5 +54,12 @@
|
||||
self.collectionView.scrollEnabled = YES;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.editionView.layer.cornerRadius = kEditionViewCornerRadius;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -15,11 +13,11 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="181"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="20U-7H-xmi" id="cFw-g7-Cgn">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="180.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="181"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" dataMode="none" translatesAutoresizingMaskIntoConstraints="NO" id="iXt-1Y-bEu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="115"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="116"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="zZI-Za-2q1">
|
||||
<size key="itemSize" width="50" height="50"/>
|
||||
@@ -29,7 +27,7 @@
|
||||
</collectionViewFlowLayout>
|
||||
</collectionView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="K7T-mO-6FZ">
|
||||
<rect key="frame" x="15" y="115.5" width="570" height="60"/>
|
||||
<rect key="frame" x="15" y="116" width="570" height="60"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="R8G-pa-NMv">
|
||||
<rect key="frame" x="160" y="0.0" width="50" height="60"/>
|
||||
@@ -37,7 +35,7 @@
|
||||
<constraint firstAttribute="width" constant="50" id="UaQ-bI-bWg"/>
|
||||
</constraints>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="directChatOff" translatesAutoresizingMaskIntoConstraints="NO" id="zv7-4U-qzL">
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_action_direct_chat" translatesAutoresizingMaskIntoConstraints="NO" id="zv7-4U-qzL">
|
||||
<rect key="frame" x="165" y="10" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="HVn-3g-jMX"/>
|
||||
@@ -47,7 +45,7 @@
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dsz-Yx-goM">
|
||||
<rect key="frame" x="210" y="0.0" width="50" height="60"/>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="notificationsOff" translatesAutoresizingMaskIntoConstraints="NO" id="LJb-Om-aCu">
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_action_notification" translatesAutoresizingMaskIntoConstraints="NO" id="LJb-Om-aCu">
|
||||
<rect key="frame" x="215" y="10" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="Bxy-g5-Fm8"/>
|
||||
@@ -57,7 +55,7 @@
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MbR-Oe-6k1">
|
||||
<rect key="frame" x="260" y="0.0" width="50" height="60"/>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="favouriteOff" translatesAutoresizingMaskIntoConstraints="NO" id="f0O-Nh-XqH">
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_action_favourite" translatesAutoresizingMaskIntoConstraints="NO" id="f0O-Nh-XqH">
|
||||
<rect key="frame" x="265" y="10" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="hss-6b-AOg"/>
|
||||
@@ -67,8 +65,8 @@
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="BsI-jp-frH">
|
||||
<rect key="frame" x="310" y="0.0" width="50" height="60"/>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="priorityLow" translatesAutoresizingMaskIntoConstraints="NO" id="uJK-LE-Vkz">
|
||||
<rect key="frame" x="320" y="10" width="40" height="40"/>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_action_priority_low" translatesAutoresizingMaskIntoConstraints="NO" id="uJK-LE-Vkz">
|
||||
<rect key="frame" x="315" y="10" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="YvB-as-G5U"/>
|
||||
<constraint firstAttribute="width" constant="40" id="ez7-fJ-00U"/>
|
||||
@@ -77,7 +75,7 @@
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="uG0-u6-BBj">
|
||||
<rect key="frame" x="360" y="0.0" width="50" height="60"/>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="leave" translatesAutoresizingMaskIntoConstraints="NO" id="cgb-3x-9qQ">
|
||||
<imageView userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_action_leave" translatesAutoresizingMaskIntoConstraints="NO" id="cgb-3x-9qQ">
|
||||
<rect key="frame" x="365" y="10" width="40" height="40"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="52j-He-vyg"/>
|
||||
@@ -115,8 +113,8 @@
|
||||
<constraint firstItem="BsI-jp-frH" firstAttribute="width" secondItem="R8G-pa-NMv" secondAttribute="width" id="mig-Wk-n7S"/>
|
||||
<constraint firstItem="dsz-Yx-goM" firstAttribute="leading" secondItem="R8G-pa-NMv" secondAttribute="trailing" id="pfB-6o-vIr"/>
|
||||
<constraint firstItem="MbR-Oe-6k1" firstAttribute="width" secondItem="R8G-pa-NMv" secondAttribute="width" id="sBc-Ow-YIB"/>
|
||||
<constraint firstItem="uJK-LE-Vkz" firstAttribute="centerX" secondItem="BsI-jp-frH" secondAttribute="centerX" id="sx8-P7-a7K"/>
|
||||
<constraint firstItem="BsI-jp-frH" firstAttribute="height" secondItem="R8G-pa-NMv" secondAttribute="height" id="ub9-qy-AoE"/>
|
||||
<constraint firstItem="uJK-LE-Vkz" firstAttribute="trailing" secondItem="BsI-jp-frH" secondAttribute="trailing" id="yTj-PM-CTK"/>
|
||||
<constraint firstItem="uG0-u6-BBj" firstAttribute="leading" secondItem="BsI-jp-frH" secondAttribute="trailing" id="zU8-FO-eQH"/>
|
||||
</constraints>
|
||||
</view>
|
||||
@@ -152,10 +150,10 @@
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="directChatOff" width="32" height="32"/>
|
||||
<image name="favouriteOff" width="33" height="29"/>
|
||||
<image name="leave" width="25" height="24"/>
|
||||
<image name="notificationsOff" width="29" height="32"/>
|
||||
<image name="priorityLow" width="31" height="24"/>
|
||||
<image name="room_action_direct_chat" width="24" height="24"/>
|
||||
<image name="room_action_favourite" width="24" height="24"/>
|
||||
<image name="room_action_leave" width="24" height="24"/>
|
||||
<image name="room_action_notification" width="24" height="24"/>
|
||||
<image name="room_action_priority_low" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
+8
-10
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -45,7 +43,7 @@
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://riot.im" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9SQ-lH-Hur">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://element.io" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9SQ-lH-Hur">
|
||||
<rect key="frame" x="0.0" y="22" width="374" height="37"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
@@ -68,13 +66,13 @@
|
||||
<rect key="frame" x="0.0" y="71" width="374" height="59"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Session ID" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uVC-qK-ET7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="17"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="37"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://riot.im" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WHf-qc-H4w">
|
||||
<rect key="frame" x="0.0" y="22" width="374" height="37"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://element.io" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WHf-qc-H4w">
|
||||
<rect key="frame" x="0.0" y="42" width="374" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -101,7 +99,7 @@
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://riot.im" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5fG-Wi-cVH">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="https://element.io" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5fG-Wi-cVH">
|
||||
<rect key="frame" x="0.0" y="22" width="374" height="37"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
// Made With Flow.
|
||||
//
|
||||
// DO NOT MODIFY, your changes will be lost when this file is regenerated.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@IBDesignable
|
||||
public class ElementView: UIView {
|
||||
public struct Defaults {
|
||||
public static let size = CGSize(width: 130.16, height: 127.75)
|
||||
public static let backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0.021)
|
||||
}
|
||||
|
||||
public var icon: UIView!
|
||||
public var _10242x: UIView!
|
||||
public var path: ShapeView!
|
||||
public var path_1: ShapeView!
|
||||
public var path_2: ShapeView!
|
||||
public var path_3: ShapeView!
|
||||
|
||||
public override var intrinsicContentSize: CGSize {
|
||||
return Defaults.size
|
||||
}
|
||||
|
||||
public override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
}
|
||||
|
||||
public required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setup()
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
backgroundColor = Defaults.backgroundColor
|
||||
clipsToBounds = false
|
||||
createViews()
|
||||
addSubviews()
|
||||
//scale(to: frame.size)
|
||||
}
|
||||
|
||||
/// Scales `self` and its subviews to `size`.
|
||||
///
|
||||
/// - Parameter size: The size `self` is scaled to.
|
||||
///
|
||||
/// UIKit specifies: "In iOS 8.0 and later, the transform property does not affect Auto Layout. Auto layout
|
||||
/// calculates a view's alignment rectangle based on its untransformed frame."
|
||||
///
|
||||
/// see: https://developer.apple.com/documentation/uikit/uiview/1622459-transform
|
||||
///
|
||||
/// If there are any constraints in IB affecting the frame of `self`, this method will have consequences on
|
||||
/// layout / rendering. To properly scale an animation, you will have to position the view manually.
|
||||
public func scale(to size: CGSize) {
|
||||
let x = size.width / Defaults.size.width
|
||||
let y = size.height / Defaults.size.height
|
||||
transform = CGAffineTransform(scaleX: x, y: y)
|
||||
}
|
||||
|
||||
private func createViews() {
|
||||
CATransaction.suppressAnimations {
|
||||
createIcon()
|
||||
create_10242x()
|
||||
createPath()
|
||||
createPath1()
|
||||
createPath2()
|
||||
createPath3()
|
||||
}
|
||||
}
|
||||
|
||||
private func createIcon() {
|
||||
icon = UIView(frame: CGRect(x: 65.21, y: 63.27, width: 120.77, height: 120.77))
|
||||
icon.backgroundColor = UIColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||
icon.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
icon.layer.shadowColor = UIColor.clear.cgColor
|
||||
icon.layer.shadowOpacity = 1
|
||||
icon.layer.position = CGPoint(x: 65.21, y: 63.27)
|
||||
icon.layer.bounds = CGRect(x: 0, y: 0, width: 120.77, height: 120.77)
|
||||
icon.layer.masksToBounds = false
|
||||
}
|
||||
|
||||
private func create_10242x() {
|
||||
_10242x = UIView(frame: CGRect(x: 60.39, y: 60.39, width: 120, height: 120))
|
||||
_10242x.backgroundColor = UIColor.clear
|
||||
_10242x.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
_10242x.layer.shadowColor = UIColor.clear.cgColor
|
||||
_10242x.layer.shadowOpacity = 1
|
||||
_10242x.layer.position = CGPoint(x: 60.39, y: 60.39)
|
||||
_10242x.layer.bounds = CGRect(x: 0, y: 0, width: 120, height: 120)
|
||||
_10242x.layer.masksToBounds = false
|
||||
}
|
||||
|
||||
private func createPath() {
|
||||
path = ShapeView(frame: CGRect(x: 70.81, y: 27.59, width: 55.2, height: 55.2))
|
||||
path.backgroundColor = UIColor.clear
|
||||
path.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
path.layer.shadowColor = UIColor.clear.cgColor
|
||||
path.layer.shadowOpacity = 1
|
||||
path.layer.position = CGPoint(x: 70.81, y: 27.59)
|
||||
path.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2)
|
||||
path.layer.masksToBounds = false
|
||||
path.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
|
||||
path.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor
|
||||
path.shapeLayer.lineDashPattern = []
|
||||
path.shapeLayer.lineDashPhase = 0
|
||||
path.shapeLayer.lineWidth = 0
|
||||
path.shapeLayer.path = CGPathCreateWithSVGString("M0,7.2c0,-3.976,3.224,-7.2,7.2,-7.2 26.51,0,48,21.49,48,48 0,3.976,-3.224,7.2,-7.2,7.2 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-18.557,-15.043,-33.6,-33.6,-33.6 -3.976,0,-7.2,-3.224,-7.2,-7.2zM0,7.2")!
|
||||
|
||||
}
|
||||
|
||||
private func createPath1() {
|
||||
path_1 = ShapeView(frame: CGRect(x: 49.2, y: 92.41, width: 55.2, height: 55.2))
|
||||
path_1.backgroundColor = UIColor.clear
|
||||
path_1.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
path_1.layer.shadowColor = UIColor.clear.cgColor
|
||||
path_1.layer.shadowOpacity = 1
|
||||
path_1.layer.position = CGPoint(x: 49.2, y: 92.41)
|
||||
path_1.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2)
|
||||
path_1.layer.masksToBounds = false
|
||||
path_1.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
|
||||
path_1.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor
|
||||
path_1.shapeLayer.lineDashPattern = []
|
||||
path_1.shapeLayer.lineDashPhase = 0
|
||||
path_1.shapeLayer.lineWidth = 0
|
||||
path_1.shapeLayer.path = CGPathCreateWithSVGString("M55.2,48c0,3.976,-3.224,7.2,-7.2,7.2 -26.51,0,-48,-21.49,-48,-48 0,-3.976,3.224,-7.2,7.2,-7.2 3.976,0,7.2,3.224,7.2,7.2 0,18.557,15.043,33.6,33.6,33.6 3.976,0,7.2,3.224,7.2,7.2zM55.2,48")!
|
||||
|
||||
}
|
||||
|
||||
private func createPath2() {
|
||||
path_2 = ShapeView(frame: CGRect(x: 27.59, y: 49.2, width: 55.2, height: 55.2))
|
||||
path_2.backgroundColor = UIColor.clear
|
||||
path_2.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
path_2.layer.shadowColor = UIColor.clear.cgColor
|
||||
path_2.layer.shadowOpacity = 1
|
||||
path_2.layer.position = CGPoint(x: 27.59, y: 49.2)
|
||||
path_2.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2)
|
||||
path_2.layer.masksToBounds = false
|
||||
path_2.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
|
||||
path_2.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor
|
||||
path_2.shapeLayer.lineDashPattern = []
|
||||
path_2.shapeLayer.lineDashPhase = 0
|
||||
path_2.shapeLayer.lineWidth = 0
|
||||
path_2.shapeLayer.path = CGPathCreateWithSVGString("M7.2,55.2c-3.976,0,-7.2,-3.224,-7.2,-7.2 0,-26.51,21.49,-48,48,-48 3.976,0,7.2,3.224,7.2,7.2 0,3.976,-3.224,7.2,-7.2,7.2 -18.557,0,-33.6,15.043,-33.6,33.6 0,3.976,-3.224,7.2,-7.2,7.2zM7.2,55.2")!
|
||||
|
||||
}
|
||||
|
||||
private func createPath3() {
|
||||
path_3 = ShapeView(frame: CGRect(x: 92.41, y: 70.81, width: 55.2, height: 55.2))
|
||||
path_3.backgroundColor = UIColor.clear
|
||||
path_3.layer.shadowOffset = CGSize(width: 0, height: 0)
|
||||
path_3.layer.shadowColor = UIColor.clear.cgColor
|
||||
path_3.layer.shadowOpacity = 1
|
||||
path_3.layer.position = CGPoint(x: 92.41, y: 70.81)
|
||||
path_3.layer.bounds = CGRect(x: 0, y: 0, width: 55.2, height: 55.2)
|
||||
path_3.layer.masksToBounds = false
|
||||
path_3.shapeLayer.fillRule = CAShapeLayerFillRule.evenOdd
|
||||
path_3.shapeLayer.fillColor = UIColor(displayP3Red: 0.052, green: 0.743, blue: 0.543, alpha: 1).cgColor
|
||||
path_3.shapeLayer.lineDashPattern = []
|
||||
path_3.shapeLayer.lineDashPhase = 0
|
||||
path_3.shapeLayer.lineWidth = 0
|
||||
path_3.shapeLayer.path = CGPathCreateWithSVGString("M48,0c3.976,0,7.2,3.224,7.2,7.2 0,26.51,-21.49,48,-48,48 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-3.976,3.224,-7.2,7.2,-7.2 18.557,0,33.6,-15.043,33.6,-33.6 0,-3.976,3.224,-7.2,7.2,-7.2zM48,0")!
|
||||
|
||||
}
|
||||
|
||||
private func addSubviews() {
|
||||
_10242x.addSubview(path)
|
||||
_10242x.addSubview(path_1)
|
||||
_10242x.addSubview(path_2)
|
||||
_10242x.addSubview(path_3)
|
||||
icon.addSubview(_10242x)
|
||||
addSubview(icon)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Made With Flow.
|
||||
//
|
||||
// DO NOT MODIFY, your changes will be lost when this file is regenerated.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
public class ElementViewController: UIViewController {
|
||||
@IBOutlet public weak var element: ElementView!
|
||||
public var timeline: Timeline_1!
|
||||
|
||||
public override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
timeline = Timeline_1(view: element, duration: 2)
|
||||
|
||||
timeline.play()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + timeline.duration) {
|
||||
self.showStartViewController()
|
||||
}
|
||||
}
|
||||
|
||||
private func showStartViewController() {
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
||||
let startViewController = storyboard.instantiateViewController(withIdentifier: "StartViewController")
|
||||
startViewController.modalPresentationStyle = .custom
|
||||
startViewController.modalTransitionStyle = .crossDissolve
|
||||
present(startViewController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright 2020 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
@objcMembers
|
||||
final class LaunchLoadingView: UIView, NibLoadable, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum LaunchAnimation {
|
||||
static let duration: TimeInterval = 3.0
|
||||
static let repeatCount = Float.greatestFiniteMagnitude
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@IBOutlet private weak var animationView: ElementView!
|
||||
private var animationTimeline: Timeline_1!
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate() -> LaunchLoadingView {
|
||||
let view = LaunchLoadingView.loadFromNib()
|
||||
return view
|
||||
}
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
let animationTimeline = Timeline_1(view: self.animationView, duration: LaunchAnimation.duration, repeatCount: LaunchAnimation.repeatCount)
|
||||
animationTimeline.play()
|
||||
self.animationTimeline = animationTimeline
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.animationView.backgroundColor = theme.backgroundColor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_0" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16097"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iN0-l3-epB" customClass="LaunchLoadingView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleAspectFit" insetsLayoutMarginsFromSafeArea="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3KG-IR-FPV" customClass="ElementView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="95" y="219" width="130" height="130"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3KG-IR-FPV" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="ig4-YX-FoT"/>
|
||||
<constraint firstItem="3KG-IR-FPV" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="r9K-7c-fjh"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="animationView" destination="3KG-IR-FPV" id="Are-fn-laY"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="136.875" y="132.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,338 @@
|
||||
// Made With Flow.
|
||||
//
|
||||
// DO NOT MODIFY, your changes will be lost when this file is regenerated.
|
||||
//
|
||||
// swiftlint:disable all
|
||||
|
||||
import UIKit
|
||||
|
||||
public class Timeline_1: Timeline {
|
||||
public convenience init(view: ElementView, duration: TimeInterval, autoreverses: Bool = false, repeatCount: Float = 0) {
|
||||
let animationsByLayer = Timeline_1.animationsByLayer(view: view, duration: duration)
|
||||
self.init(view: view, animationsByLayer: animationsByLayer, sounds: [], duration: duration, autoreverses: autoreverses, repeatCount: repeatCount)
|
||||
}
|
||||
private static func animationsByLayer(view: ElementView, duration: TimeInterval) -> [CALayer: [CAKeyframeAnimation]] {
|
||||
// Keyframe Animations for icon
|
||||
let position_x_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [65.21, 65.06, 65, 64.63]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 0.96, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeIn, .easeOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [63.27, 63.43, 63, 63.29]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 0.96, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeIn, .easeOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let transform_rotation_z_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "transform.rotation.z"
|
||||
keyframeAnimation.values = [0, 3.14159, 6.28319]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeIn, .easeOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [120.77, 141.91, 120.77]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeIn, .easeOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [120.77, 141.91, 120.77]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeIn, .easeOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let backgroundcolor_icon: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "backgroundColor"
|
||||
keyframeAnimation.values = [UIColor(red: 1, green: 1, blue: 1, alpha: 0).cgColor, UIColor.clear.cgColor]
|
||||
keyframeAnimation.keyTimes = [0, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Keyframe Animations for _10242x
|
||||
let position_x__10242x: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [60.39, 70.96, 60.39]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y__10242x: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [60.39, 70.96, 60.39]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width__10242x: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [120, 141.96, 120]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height__10242x: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [120, 141.96, 120]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Keyframe Animations for path
|
||||
let position_x_path: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [70.81, 83.73, 70.81]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y_path: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [27.59, 32.64, 27.59]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width_path: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height_path: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let path_path: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "path"
|
||||
keyframeAnimation.values = [CGPathCreateWithSVGString("M0,7.2c0,-3.976,3.224,-7.2,7.2,-7.2 26.51,0,48,21.49,48,48 0,3.976,-3.224,7.2,-7.2,7.2 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-18.557,-15.043,-33.6,-33.6,-33.6 -3.976,0,-7.2,-3.224,-7.2,-7.2zM0,7.2")!, CGPathCreateWithSVGString("M0,8.513c0,-4.702,3.812,-8.513,8.513,-8.513 31.346,0,56.757,25.411,56.757,56.757 0,4.702,-3.812,8.513,-8.513,8.513 -4.702,0,-8.513,-3.812,-8.513,-8.513 0,-21.942,-17.787,-39.73,-39.73,-39.73 -4.702,0,-8.513,-3.812,-8.513,-8.513zM0,8.513")!, CGPathCreateWithSVGString("M0,7.2c0,-3.976,3.224,-7.2,7.2,-7.2 26.51,0,48,21.49,48,48 0,3.976,-3.224,7.2,-7.2,7.2 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-18.557,-15.043,-33.6,-33.6,-33.6 -3.976,0,-7.2,-3.224,-7.2,-7.2zM0,7.2")!]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Keyframe Animations for path_1
|
||||
let position_x_path_1: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [49.2, 58.19, 49.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y_path_1: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [92.41, 109.27, 92.41]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width_path_1: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height_path_1: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let path_path_1: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "path"
|
||||
keyframeAnimation.values = [CGPathCreateWithSVGString("M55.2,48c0,3.976,-3.224,7.2,-7.2,7.2 -26.51,0,-48,-21.49,-48,-48 0,-3.976,3.224,-7.2,7.2,-7.2 3.976,0,7.2,3.224,7.2,7.2 0,18.557,15.043,33.6,33.6,33.6 3.976,0,7.2,3.224,7.2,7.2zM55.2,48")!, CGPathCreateWithSVGString("M65.27,56.757c0,4.702,-3.812,8.513,-8.513,8.513 -31.346,0,-56.757,-25.411,-56.757,-56.757 0,-4.702,3.812,-8.513,8.513,-8.513 4.702,0,8.513,3.812,8.513,8.513 0,21.942,17.787,39.73,39.73,39.73 4.702,0,8.513,3.812,8.513,8.513zM65.27,56.757")!, CGPathCreateWithSVGString("M55.2,48c0,3.976,-3.224,7.2,-7.2,7.2 -26.51,0,-48,-21.49,-48,-48 0,-3.976,3.224,-7.2,7.2,-7.2 3.976,0,7.2,3.224,7.2,7.2 0,18.557,15.043,33.6,33.6,33.6 3.976,0,7.2,3.224,7.2,7.2zM55.2,48")!]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Keyframe Animations for path_2
|
||||
let position_x_path_2: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [27.59, 32.64, 27.59]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y_path_2: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [49.2, 58.19, 49.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width_path_2: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height_path_2: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let path_path_2: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "path"
|
||||
keyframeAnimation.values = [CGPathCreateWithSVGString("M7.2,55.2c-3.976,0,-7.2,-3.224,-7.2,-7.2 0,-26.51,21.49,-48,48,-48 3.976,0,7.2,3.224,7.2,7.2 0,3.976,-3.224,7.2,-7.2,7.2 -18.557,0,-33.6,15.043,-33.6,33.6 0,3.976,-3.224,7.2,-7.2,7.2zM7.2,55.2")!, CGPathCreateWithSVGString("M8.513,65.27c-4.702,0,-8.513,-3.812,-8.513,-8.513 0,-31.346,25.411,-56.757,56.757,-56.757 4.702,0,8.513,3.812,8.513,8.513 0,4.702,-3.812,8.513,-8.513,8.513 -21.942,0,-39.73,17.787,-39.73,39.73 0,4.702,-3.812,8.513,-8.513,8.513zM8.513,65.27")!, CGPathCreateWithSVGString("M7.2,55.2c-3.976,0,-7.2,-3.224,-7.2,-7.2 0,-26.51,21.49,-48,48,-48 3.976,0,7.2,3.224,7.2,7.2 0,3.976,-3.224,7.2,-7.2,7.2 -18.557,0,-33.6,15.043,-33.6,33.6 0,3.976,-3.224,7.2,-7.2,7.2zM7.2,55.2")!]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Keyframe Animations for path_3
|
||||
let position_x_path_3: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.x"
|
||||
keyframeAnimation.values = [92.41, 109.27, 92.41]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let position_y_path_3: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "position.y"
|
||||
keyframeAnimation.values = [70.81, 83.73, 70.81]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_width_path_3: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.width"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let bounds_size_height_path_3: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "bounds.size.height"
|
||||
keyframeAnimation.values = [55.2, 65.27, 55.2]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
let path_path_3: CAKeyframeAnimation = {
|
||||
let keyframeAnimation = CAKeyframeAnimation()
|
||||
keyframeAnimation.keyPath = "path"
|
||||
keyframeAnimation.values = [CGPathCreateWithSVGString("M48,0c3.976,0,7.2,3.224,7.2,7.2 0,26.51,-21.49,48,-48,48 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-3.976,3.224,-7.2,7.2,-7.2 18.557,0,33.6,-15.043,33.6,-33.6 0,-3.976,3.224,-7.2,7.2,-7.2zM48,0")!, CGPathCreateWithSVGString("M56.757,0c4.702,0,8.513,3.812,8.513,8.513 0,31.346,-25.411,56.757,-56.757,56.757 -4.702,0,-8.513,-3.812,-8.513,-8.513 0,-4.702,3.812,-8.513,8.513,-8.513 21.942,0,39.73,-17.787,39.73,-39.73 0,-4.702,3.812,-8.513,8.513,-8.513zM56.757,0")!, CGPathCreateWithSVGString("M48,0c3.976,0,7.2,3.224,7.2,7.2 0,26.51,-21.49,48,-48,48 -3.976,0,-7.2,-3.224,-7.2,-7.2 0,-3.976,3.224,-7.2,7.2,-7.2 18.557,0,33.6,-15.043,33.6,-33.6 0,-3.976,3.224,-7.2,7.2,-7.2zM48,0")!]
|
||||
keyframeAnimation.keyTimes = [0, 0.5, 1]
|
||||
keyframeAnimation.timingFunctions = [.easeInEaseOut, .easeInEaseOut]
|
||||
keyframeAnimation.duration = duration
|
||||
|
||||
return keyframeAnimation
|
||||
}()
|
||||
|
||||
// Organize CAKeyframeAnimations by CALayer
|
||||
var animationsByLayer = [CALayer: [CAKeyframeAnimation]]()
|
||||
animationsByLayer[view._10242x.layer] = [position_x__10242x, position_y__10242x, bounds_size_height__10242x, bounds_size_width__10242x]
|
||||
animationsByLayer[view.path.layer] = [position_y_path, bounds_size_height_path, bounds_size_width_path, position_x_path, path_path]
|
||||
animationsByLayer[view.path_1.layer] = [position_y_path_1, bounds_size_width_path_1, bounds_size_height_path_1, position_x_path_1, path_path_1]
|
||||
animationsByLayer[view.path_2.layer] = [position_y_path_2, bounds_size_height_path_2, position_x_path_2, bounds_size_width_path_2, path_path_2]
|
||||
animationsByLayer[view.icon.layer] = [backgroundcolor_icon, bounds_size_width_icon, position_x_icon, position_y_icon, bounds_size_height_icon, transform_rotation_z_icon]
|
||||
animationsByLayer[view.path_3.layer] = [position_y_path_3, bounds_size_height_path_3, bounds_size_width_path_3, position_x_path_3, path_path_3]
|
||||
|
||||
return animationsByLayer
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
/// `MajorUpdateManager` is used to indicate if a major update alert should be displayed.
|
||||
@objcMembers
|
||||
final public class MajorUpdateManager: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static var lastMajorAppVersion = AppVersion(bundleShortVersion: "1.0.0", bundleVersion: "0")
|
||||
static var learnMoreStringURL = "https://element.io/previously-riot"
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var shouldShowMajorUpdate: Bool {
|
||||
guard let lastUsedAppVersion = AppVersion.lastUsed else {
|
||||
return true
|
||||
}
|
||||
return lastUsedAppVersion.compare(Constants.lastMajorAppVersion) == .orderedAscending
|
||||
}
|
||||
|
||||
var learnMoreURL: URL {
|
||||
guard let url = URL(string: Constants.learnMoreStringURL) else {
|
||||
fatalError("[MajorUpdateManager] learn more URL should be valid")
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="oj3-Ba-D5H">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Major Update View Controller-->
|
||||
<scene sceneID="0NG-cI-DUp">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="oj3-Ba-D5H" customClass="MajorUpdateViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="3k0-50-Vx9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Skx-kI-brb">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hXv-5X-Jug">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="420.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bi6-Mk-ofP">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="420.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sfc-Jg-1wf">
|
||||
<rect key="frame" x="20" y="40" width="374" height="64"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="old_logo" translatesAutoresizingMaskIntoConstraints="NO" id="0Ic-By-AMc">
|
||||
<rect key="frame" x="105" y="2.5" width="48" height="59"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="59" id="aEW-Gq-pPE"/>
|
||||
<constraint firstAttribute="width" constant="48" id="sKy-Pr-KMk"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="disclosure_icon" translatesAutoresizingMaskIntoConstraints="NO" id="DRi-Zy-VhL">
|
||||
<rect key="frame" x="184" y="26" width="6" height="12"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="6" id="N5t-0p-Iro"/>
|
||||
<constraint firstAttribute="height" constant="12" id="UZy-x6-nSw"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="launch_screen_logo" translatesAutoresizingMaskIntoConstraints="NO" id="4sW-uC-asz">
|
||||
<rect key="frame" x="217" y="0.0" width="64" height="64"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="4sW-uC-asz" secondAttribute="height" multiplier="1:1" id="O4D-SD-RKh"/>
|
||||
<constraint firstAttribute="width" constant="64" id="rXu-aJ-WRl"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="4sW-uC-asz" secondAttribute="bottom" id="2Be-Qv-NaI"/>
|
||||
<constraint firstItem="DRi-Zy-VhL" firstAttribute="centerX" secondItem="sfc-Jg-1wf" secondAttribute="centerX" id="635-Z0-wOh"/>
|
||||
<constraint firstItem="4sW-uC-asz" firstAttribute="top" secondItem="sfc-Jg-1wf" secondAttribute="top" id="OAP-1o-HP7"/>
|
||||
<constraint firstItem="4sW-uC-asz" firstAttribute="leading" secondItem="DRi-Zy-VhL" secondAttribute="trailing" constant="27" id="YET-rk-8QK"/>
|
||||
<constraint firstItem="0Ic-By-AMc" firstAttribute="centerY" secondItem="DRi-Zy-VhL" secondAttribute="centerY" id="pWJ-od-6ZE"/>
|
||||
<constraint firstItem="DRi-Zy-VhL" firstAttribute="centerY" secondItem="sfc-Jg-1wf" secondAttribute="centerY" id="qP2-VX-tQg"/>
|
||||
<constraint firstItem="DRi-Zy-VhL" firstAttribute="leading" secondItem="0Ic-By-AMc" secondAttribute="trailing" constant="31" id="tl0-z4-uy4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Riot is now Element" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LU5-Au-QYW">
|
||||
<rect key="frame" x="20" y="139" width="374" height="24"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="We're excited to announce we've changed name! Your app is up to date and you're signed in to your account." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qGD-FL-x5l">
|
||||
<rect key="frame" x="20" y="193" width="374" height="57.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wTa-n9-hnS">
|
||||
<rect key="frame" x="0.0" y="290.5" width="414" height="110"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gwX-kQ-rWD" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="Qb1-t1-eVR"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Learn more">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="learnMoreButtonAction:" destination="oj3-Ba-D5H" eventType="touchUpInside" id="OFW-zp-uty"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fxG-Q7-Y6u" userLabel="Continue">
|
||||
<rect key="frame" x="20" y="60" width="374" height="50"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="50" id="PcO-gz-cEJ"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<inset key="contentEdgeInsets" minX="20" minY="0.0" maxX="20" maxY="0.0"/>
|
||||
<state key="normal" title="Got it">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<state key="disabled">
|
||||
<color key="titleColor" red="0.47843137250000001" green="0.78823529410000004" blue="0.63137254899999995" alpha="0.5" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="doneButtonAction:" destination="oj3-Ba-D5H" eventType="touchUpInside" id="iOw-Tm-9y0"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="fxG-Q7-Y6u" firstAttribute="leading" secondItem="gwX-kQ-rWD" secondAttribute="leading" id="73y-7z-rgs"/>
|
||||
<constraint firstItem="gwX-kQ-rWD" firstAttribute="top" secondItem="wTa-n9-hnS" secondAttribute="top" id="CkR-Sj-r49"/>
|
||||
<constraint firstItem="fxG-Q7-Y6u" firstAttribute="top" secondItem="gwX-kQ-rWD" secondAttribute="bottom" constant="10" id="DkD-F0-ihM"/>
|
||||
<constraint firstAttribute="bottom" secondItem="fxG-Q7-Y6u" secondAttribute="bottom" id="Hgz-oc-fQB"/>
|
||||
<constraint firstItem="gwX-kQ-rWD" firstAttribute="centerX" secondItem="wTa-n9-hnS" secondAttribute="centerX" id="JMV-8V-ijw"/>
|
||||
<constraint firstItem="fxG-Q7-Y6u" firstAttribute="trailing" secondItem="gwX-kQ-rWD" secondAttribute="trailing" id="TVn-t3-nP5"/>
|
||||
<constraint firstItem="gwX-kQ-rWD" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="wTa-n9-hnS" secondAttribute="leading" constant="20" id="W9o-ab-txG"/>
|
||||
<constraint firstItem="gwX-kQ-rWD" firstAttribute="width" secondItem="wTa-n9-hnS" secondAttribute="width" priority="750" id="YSb-fz-5K2"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="gwX-kQ-rWD" secondAttribute="trailing" constant="20" id="czZ-nY-20t"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="sfc-Jg-1wf" firstAttribute="leading" secondItem="bi6-Mk-ofP" secondAttribute="leading" constant="20" id="Cof-JR-tAA"/>
|
||||
<constraint firstItem="qGD-FL-x5l" firstAttribute="trailing" secondItem="LU5-Au-QYW" secondAttribute="trailing" id="E3u-tM-Fih"/>
|
||||
<constraint firstAttribute="bottom" secondItem="wTa-n9-hnS" secondAttribute="bottom" constant="20" id="Eeu-4K-cFQ"/>
|
||||
<constraint firstItem="sfc-Jg-1wf" firstAttribute="top" secondItem="bi6-Mk-ofP" secondAttribute="top" constant="40" id="IPM-hy-5Zs"/>
|
||||
<constraint firstItem="wTa-n9-hnS" firstAttribute="leading" secondItem="bi6-Mk-ofP" secondAttribute="leading" id="PQu-vr-m1w"/>
|
||||
<constraint firstItem="qGD-FL-x5l" firstAttribute="leading" secondItem="LU5-Au-QYW" secondAttribute="leading" id="T2l-KM-yqu"/>
|
||||
<constraint firstItem="LU5-Au-QYW" firstAttribute="top" secondItem="sfc-Jg-1wf" secondAttribute="bottom" constant="35" id="Tic-Fp-Lbp"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="500" id="UKO-CR-38f"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sfc-Jg-1wf" secondAttribute="trailing" constant="20" id="Xx0-XH-yCe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="LU5-Au-QYW" secondAttribute="trailing" constant="20" id="Yho-f5-7sb"/>
|
||||
<constraint firstItem="qGD-FL-x5l" firstAttribute="top" secondItem="LU5-Au-QYW" secondAttribute="bottom" constant="30" id="i2U-5d-IBo"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wTa-n9-hnS" secondAttribute="trailing" id="j9i-Ft-hXR"/>
|
||||
<constraint firstItem="LU5-Au-QYW" firstAttribute="leading" secondItem="bi6-Mk-ofP" secondAttribute="leading" constant="20" id="pPv-bg-5s2"/>
|
||||
<constraint firstItem="wTa-n9-hnS" firstAttribute="top" secondItem="qGD-FL-x5l" secondAttribute="bottom" constant="40" id="sT8-vi-zTJ"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="bi6-Mk-ofP" firstAttribute="centerX" secondItem="hXv-5X-Jug" secondAttribute="centerX" id="54W-hC-vpO"/>
|
||||
<constraint firstItem="bi6-Mk-ofP" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="hXv-5X-Jug" secondAttribute="leading" id="CAZ-ov-Glh"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="bi6-Mk-ofP" secondAttribute="trailing" id="GIN-Rq-jGT"/>
|
||||
<constraint firstItem="bi6-Mk-ofP" firstAttribute="top" secondItem="hXv-5X-Jug" secondAttribute="top" id="lO9-MO-4Yw"/>
|
||||
<constraint firstAttribute="bottom" secondItem="bi6-Mk-ofP" secondAttribute="bottom" id="pOb-ih-gga"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="hXv-5X-Jug" firstAttribute="leading" secondItem="Skx-kI-brb" secondAttribute="leading" id="7Ml-eL-F1h"/>
|
||||
<constraint firstAttribute="bottom" secondItem="hXv-5X-Jug" secondAttribute="bottom" id="EpH-qw-58O"/>
|
||||
<constraint firstAttribute="trailing" secondItem="hXv-5X-Jug" secondAttribute="trailing" id="TVd-hG-BPd"/>
|
||||
<constraint firstItem="hXv-5X-Jug" firstAttribute="width" secondItem="Skx-kI-brb" secondAttribute="width" id="j65-6e-KdJ"/>
|
||||
<constraint firstItem="hXv-5X-Jug" firstAttribute="top" secondItem="Skx-kI-brb" secondAttribute="top" id="xQs-cN-JkX"/>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="Skx-kI-brb" secondAttribute="bottom" id="BUH-5s-pRT"/>
|
||||
<constraint firstItem="Skx-kI-brb" firstAttribute="leading" secondItem="YXt-eJ-QZy" secondAttribute="leading" id="HV9-gl-Mfs"/>
|
||||
<constraint firstItem="YXt-eJ-QZy" firstAttribute="trailing" secondItem="Skx-kI-brb" secondAttribute="trailing" id="Wad-zN-5zG"/>
|
||||
<constraint firstItem="YXt-eJ-QZy" firstAttribute="top" secondItem="Skx-kI-brb" secondAttribute="top" id="ijo-av-Weg"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="YXt-eJ-QZy"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="disclosureImageView" destination="DRi-Zy-VhL" id="zAE-OT-R5f"/>
|
||||
<outlet property="doneButton" destination="fxG-Q7-Y6u" id="IEi-JU-l6N"/>
|
||||
<outlet property="informationLabel" destination="qGD-FL-x5l" id="ABM-H7-cDB"/>
|
||||
<outlet property="learnMoreButton" destination="gwX-kQ-rWD" id="Wpc-Ed-XXM"/>
|
||||
<outlet property="newLogoImageView" destination="4sW-uC-asz" id="Uhw-gQ-fw7"/>
|
||||
<outlet property="oldLogoImageView" destination="0Ic-By-AMc" id="8Wg-c6-BHy"/>
|
||||
<outlet property="scrollView" destination="Skx-kI-brb" id="y6g-pG-QEp"/>
|
||||
<outlet property="titleLabel" destination="LU5-Au-QYW" id="9yp-Yf-3st"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="LRB-w0-bUp" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-3772.4637681159425" y="-774.10714285714278"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="disclosure_icon" width="6" height="12"/>
|
||||
<image name="launch_screen_logo" width="240" height="240"/>
|
||||
<image name="old_logo" width="50" height="61"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,177 @@
|
||||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh SecretsSetupRecoveryKey SecretsSetupRecoveryKey
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc
|
||||
final class MajorUpdateViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static var viewController: MajorUpdateViewController?
|
||||
static var widthConstraint: NSLayoutConstraint?
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var oldLogoImageView: UIImageView!
|
||||
@IBOutlet private weak var disclosureImageView: UIImageView!
|
||||
@IBOutlet private weak var newLogoImageView: UIImageView!
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
@IBOutlet private weak var learnMoreButton: RoundedButton!
|
||||
@IBOutlet private weak var doneButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@objc var didTapLearnMoreButton: (() -> Void)?
|
||||
@objc var didTapDoneButton: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@objc class func instantiate() -> MajorUpdateViewController {
|
||||
let viewController = StoryboardScene.MajorUpdateViewController.initialScene.instantiate()
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.setupViews()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Hide back button
|
||||
self.navigationItem.setHidesBackButton(true, animated: animated)
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
// TODO: Check image is rendered as template
|
||||
self.disclosureImageView.tintColor = theme.noticeSecondaryColor
|
||||
|
||||
self.newLogoImageView.tintColor = theme.tintColor
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.informationLabel.textColor = theme.textSecondaryColor
|
||||
|
||||
self.learnMoreButton.update(theme: theme)
|
||||
theme.applyStyle(onButton: self.doneButton)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
self.vc_removeBackTitle()
|
||||
|
||||
self.oldLogoImageView.image = Asset.Images.oldLogo.image
|
||||
self.disclosureImageView.image = Asset.Images.disclosureIcon.image
|
||||
self.newLogoImageView.image = Asset.Images.launchScreenLogo.image
|
||||
|
||||
self.titleLabel.text = VectorL10n.majorUpdateTitle
|
||||
self.informationLabel.text = VectorL10n.majorUpdateInformation
|
||||
|
||||
self.learnMoreButton.setTitle(VectorL10n.majorUpdateLearnMoreAction, for: .normal)
|
||||
self.doneButton.setTitle(VectorL10n.majorUpdateDoneAction, for: .normal)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func learnMoreButtonAction(_ sender: Any) {
|
||||
self.didTapLearnMoreButton?()
|
||||
}
|
||||
|
||||
@IBAction private func doneButtonAction(_ sender: Any) {
|
||||
self.didTapDoneButton?()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SlidingModalPresentable
|
||||
extension MajorUpdateViewController: SlidingModalPresentable {
|
||||
|
||||
func allowsDismissOnBackgroundTap() -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
|
||||
|
||||
let sizingViewContoller: MajorUpdateViewController
|
||||
|
||||
if let viewController = MajorUpdateViewController.Sizing.viewController {
|
||||
sizingViewContoller = viewController
|
||||
} else {
|
||||
sizingViewContoller = MajorUpdateViewController.instantiate()
|
||||
MajorUpdateViewController.Sizing.viewController = sizingViewContoller
|
||||
}
|
||||
|
||||
let sizingViewContollerView: UIView = sizingViewContoller.view
|
||||
|
||||
if let widthConstraint = MajorUpdateViewController.Sizing.widthConstraint {
|
||||
widthConstraint.constant = width
|
||||
} else {
|
||||
let widthConstraint = sizingViewContollerView.widthAnchor.constraint(equalToConstant: width)
|
||||
widthConstraint.isActive = true
|
||||
MajorUpdateViewController.Sizing.widthConstraint = widthConstraint
|
||||
|
||||
sizingViewContollerView.heightAnchor.constraint(equalToConstant: 0)
|
||||
}
|
||||
|
||||
sizingViewContollerView.layoutIfNeeded()
|
||||
|
||||
return sizingViewContoller.scrollView.contentSize.height
|
||||
}
|
||||
}
|
||||
@@ -277,6 +277,7 @@
|
||||
cell.bottomLeftIcon.hidden = (asset.mediaType == PHAssetMediaTypeImage);
|
||||
|
||||
cell.bottomRightIcon.image = [UIImage imageNamed:@"selection_tick"];
|
||||
cell.bottomRightIcon.tintColor = ThemeService.shared.theme.tintColor;
|
||||
cell.bottomRightIcon.hidden = !selectedAssets || (NSNotFound == [selectedAssets indexOfObject:asset]);
|
||||
|
||||
// Disable user interaction in mxkImageView, in order to let collection handle user selection
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#import "ContactTableViewCell.h"
|
||||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface PeopleViewController ()
|
||||
{
|
||||
NSInteger directRoomsSectionNumber;
|
||||
@@ -69,7 +71,7 @@
|
||||
[self addPlusButton];
|
||||
|
||||
// Apply tintColor on the (+) button
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"create_direct_chat"];
|
||||
plusButtonImageView.image = [UIImage imageNamed:@"plus_floating_action"];
|
||||
|
||||
// Register table view cell for contacts.
|
||||
[self.recentsTableView registerClass:ContactTableViewCell.class forCellReuseIdentifier:ContactTableViewCell.defaultReuseIdentifier];
|
||||
@@ -114,7 +116,7 @@
|
||||
[[MXKContactManager sharedManager] refreshLocalContacts];
|
||||
|
||||
[AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_people", @"Vector", nil);
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.riotColorOrange;
|
||||
[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
if (recentsDataSource)
|
||||
{
|
||||
|
||||
@@ -59,10 +59,7 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.reactionsBackgroundView.layer.masksToBounds = true
|
||||
|
||||
let moreReactionsImage = Asset.Images.moreReactions.image.withRenderingMode(.alwaysTemplate)
|
||||
self.moreReactionsButton.setImage(moreReactionsImage, for: .normal)
|
||||
|
||||
self.moreReactionsButton.setImage(Asset.Images.moreReactions.image, for: .normal)
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
@@ -78,7 +75,7 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable {
|
||||
func update(theme: Theme) {
|
||||
self.reactionsBackgroundView.backgroundColor = theme.headerBackgroundColor
|
||||
self.moreReactionsBackgroundView.backgroundColor = theme.headerBackgroundColor
|
||||
self.moreReactionsButton.tintColor = theme.textPrimaryColor
|
||||
self.moreReactionsButton.tintColor = theme.tintColor
|
||||
}
|
||||
|
||||
func selectionAnimationInstructionPart1() {
|
||||
|
||||
@@ -44,7 +44,6 @@ final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoad
|
||||
self.theme = theme
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.tintColor = theme.tintColor
|
||||
self.separatorView.backgroundColor = theme.lineBreakColor
|
||||
|
||||
for menuItemView in self.menuItemViews {
|
||||
menuItemView.titleColor = theme.tintColor
|
||||
@@ -74,6 +73,7 @@ final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoad
|
||||
// MARK: - Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.separatorView.isHidden = true
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
|
||||
@@ -253,10 +253,10 @@
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
self.memberHeaderView.backgroundColor = ThemeService.shared.theme.baseColor;
|
||||
self.roomMemberNameLabel.textColor = ThemeService.shared.theme.baseTextPrimaryColor;
|
||||
self.roomMemberNameLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
self.roomMemberStatusLabel.textColor = ThemeService.shared.theme.tintColor;
|
||||
self.roomMemberPowerLevelLabel.textColor = ThemeService.shared.theme.baseTextPrimaryColor;
|
||||
self.roomMemberPowerLevelLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
|
||||
// Check the table view style to select its bg color.
|
||||
self.tableView.backgroundColor = ((self.tableView.style == UITableViewStylePlain) ? ThemeService.shared.theme.backgroundColor : ThemeService.shared.theme.headerBackgroundColor);
|
||||
|
||||
@@ -169,6 +169,8 @@
|
||||
CGColorRef transparentWhiteColor = [UIColor colorWithWhite:white alpha:0].CGColor;
|
||||
tableViewMaskLayer.colors = @[(__bridge id) transparentWhiteColor, (__bridge id) transparentWhiteColor, (__bridge id) opaqueWhiteColor];
|
||||
|
||||
addParticipantButtonImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
if (self.tableView.dataSource)
|
||||
{
|
||||
[self.tableView reloadData];
|
||||
@@ -666,7 +668,7 @@
|
||||
contactsDataSource.displaySearchInputInContactsList = YES;
|
||||
contactsDataSource.forceMatrixIdInDisplayName = YES;
|
||||
// Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user.
|
||||
contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
|
||||
contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor];
|
||||
|
||||
// List all the participants matrix user id to ignore them during the contacts search.
|
||||
for (Contact *contact in actualParticipants)
|
||||
@@ -1748,10 +1750,7 @@
|
||||
effectBackgroundBottom.hidden = YES;
|
||||
|
||||
// place holder
|
||||
searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder
|
||||
attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
||||
NSUnderlineColorAttributeName: ThemeService.shared.theme.tintColor,
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}];
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.searchPlaceholderColor;
|
||||
}
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
// The jump to last unread banner
|
||||
@property (weak, nonatomic) IBOutlet UIView *jumpToLastUnreadBannerContainer;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *jumpToLastUnreadBannerContainerTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *jumpToLastUnreadImageView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *jumpToLastUnreadButton;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *jumpToLastUnreadLabel;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *resetReadMarkerButton;
|
||||
|
||||
@@ -435,6 +435,8 @@
|
||||
self.errorPresenter = [MXKErrorAlertPresentation new];
|
||||
self.roomMessageURLParser = [RoomMessageURLParser new];
|
||||
|
||||
self.jumpToLastUnreadLabel.text = NSLocalizedStringFromTable(@"room_jump_to_first_unread", @"Vector", nil);
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
@@ -470,9 +472,9 @@
|
||||
|
||||
// Prepare jump to last unread banner
|
||||
self.jumpToLastUnreadBannerContainer.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
self.jumpToLastUnreadLabel.attributedText = [[NSAttributedString alloc] initWithString:NSLocalizedStringFromTable(@"room_jump_to_first_unread", @"Vector", nil) attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), NSUnderlineColorAttributeName: ThemeService.shared.theme.textPrimaryColor, NSForegroundColorAttributeName: ThemeService.shared.theme.textPrimaryColor}];
|
||||
self.jumpToLastUnreadBannerSeparatorView.backgroundColor = ThemeService.shared.theme.headerBorderColor;
|
||||
|
||||
self.jumpToLastUnreadImageView.tintColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.jumpToLastUnreadLabel.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.jumpToLastUnreadBannerSeparatorView.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
||||
|
||||
self.expandedHeaderContainer.backgroundColor = ThemeService.shared.theme.baseColor;
|
||||
self.previewHeaderContainer.backgroundColor = ThemeService.shared.theme.headerBackgroundColor;
|
||||
@@ -1488,7 +1490,7 @@
|
||||
else
|
||||
{
|
||||
// Reset original icon
|
||||
self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"apps-icon"];
|
||||
self.navigationItem.rightBarButtonItems[1].image = [UIImage imageNamed:@"integrations_icon"];
|
||||
self.navigationItem.rightBarButtonItems[1].accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_integrations", @"Vector", nil);
|
||||
}
|
||||
|
||||
@@ -3402,7 +3404,7 @@
|
||||
contactsDataSource.displaySearchInputInContactsList = YES;
|
||||
contactsDataSource.forceMatrixIdInDisplayName = YES;
|
||||
// Add a plus icon to the contact cell in the contacts picker, in order to make it more understandable for the end user.
|
||||
contactsDataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
|
||||
contactsDataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor];
|
||||
|
||||
// List all the participants matrix user id to ignore them during the contacts search.
|
||||
NSArray *members = [self.roomDataSource.roomState.members membersWithoutConferenceUser];
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -21,6 +19,7 @@
|
||||
<outlet property="jumpToLastUnreadBannerContainerTopConstraint" destination="5eM-eJ-khq" id="b1J-aM-ZcT"/>
|
||||
<outlet property="jumpToLastUnreadBannerSeparatorView" destination="knN-q1-QkJ" id="hHJ-c8-QfN"/>
|
||||
<outlet property="jumpToLastUnreadButton" destination="ISb-UT-u0O" id="fs0-sQ-lRe"/>
|
||||
<outlet property="jumpToLastUnreadImageView" destination="Vlz-UJ-Jz8" id="8iM-Go-SqC"/>
|
||||
<outlet property="jumpToLastUnreadLabel" destination="S1q-B4-Df3" id="McV-gv-bUa"/>
|
||||
<outlet property="overlayContainerView" destination="gt1-EO-UVY" id="5q6-pW-UyZ"/>
|
||||
<outlet property="previewHeaderContainer" destination="54r-18-K1g" id="Klt-RV-V1E"/>
|
||||
@@ -47,7 +46,7 @@
|
||||
</userDefinedRuntimeAttributes>
|
||||
</tableView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uK2-9a-rZj" userLabel="Expanded Header Container" customClass="ExpandedRoomTitleView">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="215"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="215"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomVCExpandedHeaderContainer"/>
|
||||
<constraints>
|
||||
@@ -74,7 +73,7 @@
|
||||
<action selector="onButtonPressed:" destination="-1" eventType="touchUpInside" id="7pe-19-Zxc"/>
|
||||
</connections>
|
||||
</button>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="jump_to_unread" translatesAutoresizingMaskIntoConstraints="NO" id="Vlz-UJ-Jz8">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="scrollup" translatesAutoresizingMaskIntoConstraints="NO" id="Vlz-UJ-Jz8">
|
||||
<rect key="frame" x="13" y="7" width="30" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="30" id="JCI-mP-w3F"/>
|
||||
@@ -105,7 +104,7 @@
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="knN-q1-QkJ" userLabel="Separator View">
|
||||
<rect key="frame" x="10" y="43" width="365" height="1"/>
|
||||
<rect key="frame" x="0.0" y="43" width="375" height="1"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="1" id="8k7-fr-b8R"/>
|
||||
@@ -117,7 +116,7 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="7TA-9m-DJm"/>
|
||||
<constraint firstItem="ISb-UT-u0O" firstAttribute="trailing" secondItem="S1q-B4-Df3" secondAttribute="trailing" id="ABS-rW-2Up"/>
|
||||
<constraint firstItem="knN-q1-QkJ" firstAttribute="leading" secondItem="S6r-bo-jxw" secondAttribute="leading" constant="10" id="DL5-gC-Y2y"/>
|
||||
<constraint firstItem="knN-q1-QkJ" firstAttribute="leading" secondItem="S6r-bo-jxw" secondAttribute="leading" id="DL5-gC-Y2y"/>
|
||||
<constraint firstAttribute="bottom" secondItem="knN-q1-QkJ" secondAttribute="bottom" id="HrB-BI-pbX"/>
|
||||
<constraint firstItem="Vlz-UJ-Jz8" firstAttribute="centerY" secondItem="S6r-bo-jxw" secondAttribute="centerY" id="Oyl-YW-LyY"/>
|
||||
<constraint firstItem="c4g-BY-xOo" firstAttribute="centerY" secondItem="TYG-1i-OrY" secondAttribute="centerY" id="PY6-yg-tuv"/>
|
||||
@@ -149,7 +148,7 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gt1-EO-UVY">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@@ -188,6 +187,6 @@
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="cancel" width="20" height="20"/>
|
||||
<image name="jump_to_unread" width="30" height="30"/>
|
||||
<image name="scrollup" width="30" height="30"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -2526,7 +2526,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
addAddressCell.mxkLabel.text = nil;
|
||||
|
||||
addAddressCell.accessoryType = UITableViewCellAccessoryNone;
|
||||
addAddressCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
addAddressCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
|
||||
|
||||
addAddressTextField = addAddressCell.mxkTextField;
|
||||
addAddressTextField.placeholder = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_details_new_address_placeholder", @"Vector", nil), self.mainSession.matrixRestClient.homeserverSuffix];
|
||||
@@ -2614,7 +2614,7 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
|
||||
addCommunityCell.mxkLabel.text = nil;
|
||||
|
||||
addCommunityCell.accessoryType = UITableViewCellAccessoryNone;
|
||||
addCommunityCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
addCommunityCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
|
||||
|
||||
addGroupTextField = addCommunityCell.mxkTextField;
|
||||
addGroupTextField.placeholder = [NSString stringWithFormat:NSLocalizedStringFromTable(@"room_details_new_flair_placeholder", @"Vector", nil), self.mainSession.matrixRestClient.homeserverSuffix];
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
[super customizeTableViewCellRendering];
|
||||
|
||||
_label.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
self.checkBox.tintColor = ThemeService.shared.theme.tintColor;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)enabled
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#import "TableViewCellWithCheckBoxes.h"
|
||||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
// The space between 2 check boxes
|
||||
#define TABLEVIEWCELLWITHCHECKBOXES_MARGIN 8
|
||||
|
||||
@@ -130,6 +132,7 @@
|
||||
|
||||
// Store the new check box unselected by default
|
||||
checkbox.image = [UIImage imageNamed:@"selection_untick"];
|
||||
checkbox.tintColor = ThemeService.shared.theme.tintColor;
|
||||
checkbox.tag = 0;
|
||||
[checkBoxesArray addObject:checkbox];
|
||||
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
}
|
||||
|
||||
self.iconImageView.image = [UIImage imageNamed:@"error"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
self.iconImageView.hidden = NO;
|
||||
|
||||
if (onIconTapGesture)
|
||||
@@ -187,6 +188,7 @@
|
||||
if (labelText.length)
|
||||
{
|
||||
self.iconImageView.image = [UIImage imageNamed:@"error"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor;
|
||||
self.messageLabel.text = labelText;
|
||||
self.messageLabel.textColor = ThemeService.shared.theme.warningColor;
|
||||
|
||||
@@ -204,6 +206,7 @@
|
||||
if (labelText.length)
|
||||
{
|
||||
self.iconImageView.image = [UIImage imageNamed:@"typing"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.tintColor;
|
||||
self.messageLabel.text = labelText;
|
||||
|
||||
self.iconImageView.hidden = NO;
|
||||
@@ -284,7 +287,8 @@
|
||||
{
|
||||
[self reset];
|
||||
|
||||
self.iconImageView.image = [UIImage imageNamed:@"newmessages"];
|
||||
self.iconImageView.image = [UIImage imageNamed:@"scrolldown"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor;
|
||||
|
||||
NSString *notification;
|
||||
if (newMessagesCount > 1)
|
||||
@@ -305,6 +309,7 @@
|
||||
[self resetIcon];
|
||||
|
||||
self.iconImageView.image = [UIImage imageNamed:@"scrolldown"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
}
|
||||
self.iconImageView.hidden = NO;
|
||||
|
||||
@@ -376,6 +381,7 @@
|
||||
self.messageTextView.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.iconImageView.image = [UIImage imageNamed:@"error"];
|
||||
self.iconImageView.tintColor = ThemeService.shared.theme.noticeColor;
|
||||
self.iconImageView.hidden = NO;
|
||||
|
||||
[self checkHeight:YES];
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
self.separatorView.backgroundColor = ThemeService.shared.theme.lineBreakColor;
|
||||
|
||||
|
||||
// Custom the growingTextView display
|
||||
growingTextView.layer.cornerRadius = 0;
|
||||
growingTextView.layer.borderWidth = 0;
|
||||
@@ -96,6 +96,10 @@
|
||||
self.attachMediaButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_upload", @"Vector", nil);
|
||||
self.voiceCallButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_call", @"Vector", nil);
|
||||
self.hangupCallButton.accessibilityLabel = NSLocalizedStringFromTable(@"room_accessibility_hangup", @"Vector", nil);
|
||||
|
||||
self.hangupCallButton.tintColor = ThemeService.shared.theme.noticeColor;
|
||||
self.voiceCallButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
self.attachMediaButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -92,10 +89,10 @@
|
||||
<constraint firstAttribute="width" secondItem="Owf-M8-qJi" secondAttribute="height" multiplier="1:1" id="1Ni-y7-Nsa"/>
|
||||
<constraint firstAttribute="width" constant="46" id="9FZ-CI-diT"/>
|
||||
</constraints>
|
||||
<state key="normal" image="voice_call_icon">
|
||||
<state key="normal" image="voice_call_hangon_icon">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="voice_call_icon"/>
|
||||
<state key="highlighted" image="voice_call_hangon_icon"/>
|
||||
<connections>
|
||||
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="Cxg-BO-TfK"/>
|
||||
</connections>
|
||||
@@ -106,10 +103,10 @@
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="2nY-YP-BvA" secondAttribute="height" multiplier="1:1" id="59L-Jf-tXO"/>
|
||||
</constraints>
|
||||
<state key="normal" image="call_hangup_icon">
|
||||
<state key="normal" image="voice_call_hangup_icon">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<state key="highlighted" image="call_hangup_icon"/>
|
||||
<state key="highlighted" image="voice_call_hangup_icon"/>
|
||||
<connections>
|
||||
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="PB3-d8-Kbf"/>
|
||||
</connections>
|
||||
@@ -173,12 +170,13 @@
|
||||
<outlet property="voiceCallButton" destination="Owf-M8-qJi" id="krT-w1-mfT"/>
|
||||
<outlet property="voiceCallButtonWidthConstraint" destination="9FZ-CI-diT" id="jYt-Pe-8DY"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="139" y="152"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="call_hangup_icon" width="26" height="27"/>
|
||||
<image name="e2e_unencrypted" width="14" height="12"/>
|
||||
<image name="upload_icon" width="21" height="26"/>
|
||||
<image name="voice_call_icon" width="24" height="27"/>
|
||||
<image name="upload_icon" width="24" height="24"/>
|
||||
<image name="voice_call_hangon_icon" width="24" height="24"/>
|
||||
<image name="voice_call_hangup_icon" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -30,29 +30,14 @@
|
||||
bundle:[NSBundle bundleForClass:[self class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.membersListIcon.image = [MXKTools paintImage:self.membersListIcon.image
|
||||
withColor:ThemeService.shared.theme.tintColor];
|
||||
|
||||
// TODO: paintImage does not work here because addParticipantIcon has 2 colors
|
||||
// self.addParticipantIcon.image = [MXKTools paintImage:self.addParticipantIcon.image
|
||||
// withColor:ThemeService.shared.theme.accent];
|
||||
}
|
||||
|
||||
-(void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
self.roomTopic.textColor = ThemeService.shared.theme.baseTextSecondaryColor;
|
||||
self.roomMembers.textColor = ThemeService.shared.theme.tintColor;
|
||||
self.membersListIcon.tintColor = ThemeService.shared.theme.tintColor;
|
||||
self.addParticipantIcon.tintColor = ThemeService.shared.theme.tintColor;
|
||||
}
|
||||
|
||||
- (void)refreshDisplay
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@@ -46,7 +44,7 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" text="Room Name" textAlignment="center" adjustsFontSizeToFit="NO" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="6uH-I3-RQg">
|
||||
<rect key="frame" x="247.5" y="103" width="105" height="23"/>
|
||||
<rect key="frame" x="249.5" y="103" width="101" height="23"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="DisplayNameTextField"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="18"/>
|
||||
@@ -69,12 +67,12 @@
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="members_list_icon" translatesAutoresizingMaskIntoConstraints="NO" id="S3Y-wJ-HOe">
|
||||
<rect key="frame" x="260" y="163" width="15" height="15"/>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="members_list_icon" translatesAutoresizingMaskIntoConstraints="NO" id="S3Y-wJ-HOe">
|
||||
<rect key="frame" x="252" y="160.5" width="20" height="20"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomMembersDetailsIcon"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="15" id="XTx-6p-2wB"/>
|
||||
<constraint firstAttribute="height" constant="15" id="tXh-eB-ave"/>
|
||||
<constraint firstAttribute="width" constant="20" id="XTx-6p-2wB"/>
|
||||
<constraint firstAttribute="height" constant="20" id="tXh-eB-ave"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ckb-7c-sTg">
|
||||
@@ -95,10 +93,10 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tsg-nl-P3k">
|
||||
<rect key="frame" x="322" y="151.5" width="38" height="38"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="add_participant" translatesAutoresizingMaskIntoConstraints="NO" id="i40-fd-AlH">
|
||||
<rect key="frame" x="10" y="10" width="18" height="18"/>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="add_participant" translatesAutoresizingMaskIntoConstraints="NO" id="i40-fd-AlH">
|
||||
<rect key="frame" x="9" y="9" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="18" id="Jxs-fk-U04"/>
|
||||
<constraint firstAttribute="width" constant="20" id="Jxs-fk-U04"/>
|
||||
<constraint firstAttribute="width" secondItem="i40-fd-AlH" secondAttribute="height" multiplier="1:1" id="cuB-Hn-Dl3"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
@@ -141,7 +139,7 @@
|
||||
<constraint firstItem="uSp-YH-L18" firstAttribute="top" secondItem="BkF-x3-7fX" secondAttribute="top" id="dn3-Hv-OyX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Ckb-7c-sTg" secondAttribute="trailing" id="erV-R4-LXD"/>
|
||||
<constraint firstItem="6uH-I3-RQg" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="BkF-x3-7fX" secondAttribute="leading" constant="31" id="gnq-cO-l4Y"/>
|
||||
<constraint firstItem="ou0-3Z-weL" firstAttribute="leading" secondItem="S3Y-wJ-HOe" secondAttribute="trailing" constant="7" id="iAL-UV-5nu"/>
|
||||
<constraint firstItem="ou0-3Z-weL" firstAttribute="leading" secondItem="S3Y-wJ-HOe" secondAttribute="trailing" constant="10" id="iAL-UV-5nu"/>
|
||||
<constraint firstItem="qD3-kA-DSI" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="BkF-x3-7fX" secondAttribute="leading" constant="31" id="jjG-gN-eIv"/>
|
||||
<constraint firstItem="6uH-I3-RQg" firstAttribute="top" secondItem="uSp-YH-L18" secondAttribute="bottom" constant="8" id="rAh-mV-nb5"/>
|
||||
<constraint firstItem="ou0-3Z-weL" firstAttribute="top" secondItem="qD3-kA-DSI" secondAttribute="bottom" constant="14" id="sJB-Wr-gFl"/>
|
||||
@@ -170,6 +168,6 @@
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="add_participant" width="30" height="30"/>
|
||||
<image name="members_list_icon" width="15" height="12"/>
|
||||
<image name="members_list_icon" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -74,8 +74,7 @@
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
self.roomDetailsIconImageView.image = [MXKTools paintImage:self.roomDetailsIconImageView.image
|
||||
withColor:ThemeService.shared.theme.tintColor];
|
||||
self.roomDetailsIconImageView.image = self.roomDetailsIconImageView.image;
|
||||
|
||||
if (self.superview)
|
||||
{
|
||||
@@ -140,7 +139,8 @@
|
||||
[super customizeViewRendering];
|
||||
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.baseTextPrimaryColor : ThemeService.shared.theme.textSecondaryColor);
|
||||
self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.textPrimaryColor : ThemeService.shared.theme.textSecondaryColor);
|
||||
self.roomDetailsIconImageView.tintColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
}
|
||||
|
||||
- (void)setRoomPreviewData:(RoomPreviewData *)roomPreviewData
|
||||
@@ -169,7 +169,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
self.displayNameTextField.textColor = ThemeService.shared.theme.baseTextPrimaryColor;
|
||||
self.displayNameTextField.textColor = ThemeService.shared.theme.textPrimaryColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ static CGFloat const kTextFontSize = 15.0;
|
||||
self.activityIndicator.backgroundColor = ThemeService.shared.theme.overlayBackgroundColor;
|
||||
|
||||
self.view.backgroundColor = ThemeService.shared.theme.backgroundColor;
|
||||
|
||||
self.forgetMessageButton.tintColor = ThemeService.shared.theme.tintColor;
|
||||
|
||||
[self updateStringAttributes];
|
||||
[self updateNavigationBar];
|
||||
|
||||
@@ -1486,8 +1486,7 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
newEmailCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_add_email_address", @"Vector", nil);
|
||||
newEmailCell.mxkTextField.text = nil;
|
||||
newEmailCell.mxkTextField.userInteractionEnabled = NO;
|
||||
|
||||
newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1522,7 +1521,7 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
newEmailTextField = newEmailCell.mxkTextField;
|
||||
}
|
||||
|
||||
UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:ThemeService.shared.theme.tintColor];
|
||||
UIImage *accessoryViewImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor];
|
||||
newEmailCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage];
|
||||
}
|
||||
|
||||
@@ -1551,7 +1550,7 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
newPhoneCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_add_phone_number", @"Vector", nil);
|
||||
newPhoneCell.mxkTextField.text = nil;
|
||||
newPhoneCell.mxkTextField.userInteractionEnabled = NO;
|
||||
newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"plus_icon"]];
|
||||
newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:[[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor]];
|
||||
|
||||
cell = newPhoneCell;
|
||||
}
|
||||
@@ -1614,7 +1613,7 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
newPhoneNumberCell = newPhoneCell;
|
||||
}
|
||||
|
||||
UIImage *accessoryViewImage = [MXKTools paintImage:[UIImage imageNamed:@"plus_icon"] withColor:ThemeService.shared.theme.tintColor];
|
||||
UIImage *accessoryViewImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.tintColor];
|
||||
newPhoneCell.accessoryView = [[UIImageView alloc] initWithImage:accessoryViewImage];
|
||||
|
||||
cell = newPhoneCell;
|
||||
@@ -2556,7 +2555,8 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
if (row == OTHER_COPYRIGHT_INDEX)
|
||||
{
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:NSLocalizedStringFromTable(@"settings_copyright_url", @"Vector", nil)];
|
||||
NSString *copyrightUrlString = [[NSUserDefaults standardUserDefaults] objectForKey:@"settingsCopyrightUrl"];
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:copyrightUrlString];
|
||||
|
||||
webViewViewController.title = NSLocalizedStringFromTable(@"settings_copyright", @"Vector", nil);
|
||||
|
||||
@@ -2564,7 +2564,8 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
}
|
||||
else if (row == OTHER_TERM_CONDITIONS_INDEX)
|
||||
{
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:NSLocalizedStringFromTable(@"settings_term_conditions_url", @"Vector", nil)];
|
||||
NSString *termsConditionsUrlString = [[NSUserDefaults standardUserDefaults] objectForKey:@"settingsTermsConditionsUrl"];
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:termsConditionsUrlString];
|
||||
|
||||
webViewViewController.title = NSLocalizedStringFromTable(@"settings_term_conditions", @"Vector", nil);
|
||||
|
||||
@@ -2572,7 +2573,8 @@ SettingsIdentityServerCoordinatorBridgePresenterDelegate>
|
||||
}
|
||||
else if (row == OTHER_PRIVACY_INDEX)
|
||||
{
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:NSLocalizedStringFromTable(@"settings_privacy_policy_url", @"Vector", nil)];
|
||||
NSString *privacyPolicyUrlString = [[NSUserDefaults standardUserDefaults] objectForKey:@"settingsPrivacyPolicyUrl"];
|
||||
WebViewViewController *webViewViewController = [[WebViewViewController alloc] initWithURL:privacyPolicyUrlString];
|
||||
|
||||
webViewViewController.title = NSLocalizedStringFromTable(@"settings_privacy_policy", @"Vector", nil);
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
dataSource.forceMatrixIdInDisplayName = YES;
|
||||
// Add a plus icon to the contact cell when a search session is in progress,
|
||||
// in order to make it more understandable for the end user.
|
||||
dataSource.contactCellAccessoryImage = [UIImage imageNamed:@"plus_icon"];
|
||||
dataSource.contactCellAccessoryImage = [[UIImage imageNamed:@"plus_icon"] vc_tintedImageUsingColor:ThemeService.shared.theme.textPrimaryColor];
|
||||
|
||||
[self displayList:dataSource];
|
||||
|
||||
@@ -639,10 +639,7 @@
|
||||
// place holder
|
||||
if (searchBarTextField.placeholder)
|
||||
{
|
||||
searchBarTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:searchBarTextField.placeholder
|
||||
attributes:@{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
|
||||
NSUnderlineColorAttributeName: ThemeService.shared.theme.tintColor,
|
||||
NSForegroundColorAttributeName: ThemeService.shared.theme.tintColor}];
|
||||
searchBarTextField.textColor = ThemeService.shared.theme.placeholderTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,8 @@
|
||||
}
|
||||
|
||||
[self presentReviewSessionsAlertIfNeeded];
|
||||
|
||||
[[AppDelegate theDelegate] checkAppVersion];
|
||||
}
|
||||
|
||||
if (unifiedSearchViewController)
|
||||
|
||||
Reference in New Issue
Block a user