Merge branch 'develop' into riot_3396

This commit is contained in:
ismailgulek
2020-07-16 10:38:42 +03:00
462 changed files with 4756 additions and 1594 deletions
@@ -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];
}
+2
View File
@@ -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
+52 -16
View File
@@ -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
+35 -26
View File
@@ -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>
+5 -3
View File
@@ -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];
+151
View File
@@ -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()
}
}
+310
View File
@@ -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
}
}
+77
View File
@@ -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
}
}
+31
View File
@@ -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()
}
}
}
+30
View File
@@ -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
}
}
+144
View File
@@ -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)
{
+13 -5
View File
@@ -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>
@@ -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>
+338
View File
@@ -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
+4 -2
View File
@@ -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
+1
View File
@@ -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;
+7 -5
View File
@@ -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];
+10 -11
View File
@@ -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];
+10 -8
View File
@@ -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)