mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-22 01:22:46 +02:00
Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
#import "MXKImageView.h"
|
||||
#import "MXKAccount.h"
|
||||
|
||||
/**
|
||||
MXKAccountTableViewCell instance is a table view cell used to display a matrix user.
|
||||
*/
|
||||
@interface MXKAccountTableViewCell : MXKTableViewCell
|
||||
|
||||
/**
|
||||
The displayed account
|
||||
*/
|
||||
@property (nonatomic) MXKAccount* mxAccount;
|
||||
|
||||
/**
|
||||
The default account picture displayed when no picture is defined.
|
||||
*/
|
||||
@property (nonatomic) UIImage *picturePlaceholder;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet MXKImageView* accountPicture;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel* accountDisplayName;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UISwitch* accountSwitchToggle;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKAccountTableViewCell.h"
|
||||
|
||||
@import MatrixSDK.MXMediaManager;
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
@implementation MXKAccountTableViewCell
|
||||
|
||||
- (void)customizeTableViewCellRendering
|
||||
{
|
||||
[super customizeTableViewCellRendering];
|
||||
|
||||
self.accountPicture.defaultBackgroundColor = [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (void)setMxAccount:(MXKAccount *)mxAccount
|
||||
{
|
||||
UIColor *presenceColor = nil;
|
||||
|
||||
_accountDisplayName.text = mxAccount.fullDisplayName;
|
||||
|
||||
if (mxAccount.mxSession)
|
||||
{
|
||||
_accountPicture.mediaFolder = kMXMediaManagerAvatarThumbnailFolder;
|
||||
_accountPicture.enableInMemoryCache = YES;
|
||||
[_accountPicture setImageURI:mxAccount.userAvatarUrl
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
toFitViewSize:_accountPicture.frame.size
|
||||
withMethod:MXThumbnailingMethodCrop
|
||||
previewImage:self.picturePlaceholder
|
||||
mediaManager:mxAccount.mxSession.mediaManager];
|
||||
|
||||
presenceColor = [MXKAccount presenceColor:mxAccount.userPresence];
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountPicture.image = self.picturePlaceholder;
|
||||
}
|
||||
|
||||
if (presenceColor)
|
||||
{
|
||||
_accountPicture.layer.borderWidth = 2;
|
||||
_accountPicture.layer.borderColor = presenceColor.CGColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountPicture.layer.borderWidth = 0;
|
||||
}
|
||||
|
||||
_accountSwitchToggle.on = !mxAccount.disabled;
|
||||
if (mxAccount.disabled)
|
||||
{
|
||||
_accountDisplayName.textColor = [UIColor lightGrayColor];
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountDisplayName.textColor = [UIColor blackColor];
|
||||
}
|
||||
|
||||
_mxAccount = mxAccount;
|
||||
}
|
||||
|
||||
- (UIImage*)picturePlaceholder
|
||||
{
|
||||
return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// Round image view
|
||||
[_accountPicture.layer setCornerRadius:_accountPicture.frame.size.width / 2];
|
||||
_accountPicture.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="odA-la-SwE" customClass="MXKAccountTableViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="odA-la-SwE" id="Ice-YN-Ffy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v7q-rw-kph" customClass="MXKImageView">
|
||||
<rect key="frame" x="5" y="5" width="39" height="39"/>
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="v7q-rw-kph" secondAttribute="height" multiplier="1:1" id="FNp-g0-6gK"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rfi-Fa-7Uq">
|
||||
<rect key="frame" x="54" y="14" width="536" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="k0G-KZ-5ft">
|
||||
<rect key="frame" x="543" y="9" width="51" height="31"/>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="rfi-Fa-7Uq" secondAttribute="trailing" constant="10" id="TxZ-NW-Qjo"/>
|
||||
<constraint firstAttribute="centerY" secondItem="rfi-Fa-7Uq" secondAttribute="centerY" id="Z4K-cO-leY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="v7q-rw-kph" secondAttribute="bottom" constant="5" id="ddM-yA-zDv"/>
|
||||
<constraint firstItem="v7q-rw-kph" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="5" id="gWc-fO-98S"/>
|
||||
<constraint firstAttribute="centerY" secondItem="k0G-KZ-5ft" secondAttribute="centerY" id="iEh-VQ-sVC"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k0G-KZ-5ft" secondAttribute="trailing" constant="8" id="msS-z1-wdL"/>
|
||||
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="leading" secondItem="v7q-rw-kph" secondAttribute="trailing" constant="10" id="p61-62-Veh"/>
|
||||
<constraint firstItem="v7q-rw-kph" firstAttribute="top" secondItem="Ice-YN-Ffy" secondAttribute="top" constant="5" id="yf2-Mb-AeI"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accountDisplayName" destination="rfi-Fa-7Uq" id="Tfu-Fo-eWp"/>
|
||||
<outlet property="accountPicture" destination="v7q-rw-kph" id="Ss6-iD-9vq"/>
|
||||
<outlet property="accountSwitchToggle" destination="k0G-KZ-5ft" id="k4E-FL-XyR"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKAuthInputsView.h"
|
||||
|
||||
@interface MXKAuthInputsEmailCodeBasedView : MXKAuthInputsView
|
||||
|
||||
/**
|
||||
The input text field related to user id or user login.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField;
|
||||
|
||||
/**
|
||||
The input text field used to fill an email or the related token.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *emailAndTokenTextField;
|
||||
|
||||
/**
|
||||
Label used to prompt user to fill the email token.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UILabel *promptEmailTokenLabel;
|
||||
|
||||
/**
|
||||
The text field related to the display name. This item is displayed in case of registration.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKAuthInputsEmailCodeBasedView.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@implementation MXKAuthInputsEmailCodeBasedView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKAuthInputsEmailCodeBasedView class])
|
||||
bundle:[NSBundle bundleForClass:[MXKAuthInputsEmailCodeBasedView class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
_userLoginTextField.placeholder = [MatrixKitL10n loginUserIdPlaceholder];
|
||||
_emailAndTokenTextField.placeholder = [MatrixKitL10n loginEmailPlaceholder];
|
||||
_promptEmailTokenLabel.text = [MatrixKitL10n loginPromptEmailToken];
|
||||
|
||||
_displayNameTextField.placeholder = [MatrixKitL10n loginDisplayNamePlaceholder];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
||||
{
|
||||
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
// Validate first the provided session
|
||||
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
|
||||
|
||||
if ([super setAuthSession:validSession withAuthType:authType])
|
||||
{
|
||||
// Set initial layout
|
||||
self.userLoginTextField.hidden = NO;
|
||||
self.promptEmailTokenLabel.hidden = YES;
|
||||
|
||||
if (type == MXKAuthenticationTypeLogin)
|
||||
{
|
||||
self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone;
|
||||
self.displayNameTextField.hidden = YES;
|
||||
|
||||
self.viewHeightConstraint.constant = self.displayNameTextField.frame.origin.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.emailAndTokenTextField.returnKeyType = UIReturnKeyNext;
|
||||
self.displayNameTextField.hidden = NO;
|
||||
|
||||
self.viewHeightConstraint.constant = 122;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString*)validateParameters
|
||||
{
|
||||
NSString *errorMsg = [super validateParameters];
|
||||
|
||||
if (!errorMsg)
|
||||
{
|
||||
if (!self.areAllRequiredFieldsSet)
|
||||
{
|
||||
errorMsg = [MatrixKitL10n loginInvalidParam];
|
||||
}
|
||||
}
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
- (BOOL)areAllRequiredFieldsSet
|
||||
{
|
||||
BOOL ret = [super areAllRequiredFieldsSet];
|
||||
|
||||
// Check required fields //FIXME what are required fields in this authentication flow?
|
||||
ret = (ret && self.userLoginTextField.text.length && self.emailAndTokenTextField.text.length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
[self.userLoginTextField resignFirstResponder];
|
||||
[self.emailAndTokenTextField resignFirstResponder];
|
||||
[self.displayNameTextField resignFirstResponder];
|
||||
|
||||
[super dismissKeyboard];
|
||||
}
|
||||
|
||||
- (void)nextStep
|
||||
{
|
||||
// Consider here the email token has been requested with success
|
||||
[super nextStep];
|
||||
|
||||
self.userLoginTextField.hidden = YES;
|
||||
self.promptEmailTokenLabel.hidden = NO;
|
||||
self.emailAndTokenTextField.placeholder = nil;
|
||||
self.emailAndTokenTextField.returnKeyType = UIReturnKeyDone;
|
||||
|
||||
self.displayNameTextField.hidden = YES;
|
||||
}
|
||||
|
||||
- (NSString*)userId
|
||||
{
|
||||
return self.userLoginTextField.text;
|
||||
}
|
||||
|
||||
#pragma mark UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
if (textField.returnKeyType == UIReturnKeyDone)
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[textField resignFirstResponder];
|
||||
|
||||
// Launch authentication now
|
||||
[self.delegate authInputsViewDidPressDoneKey:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
//"Next" key has been pressed
|
||||
if (textField == self.userLoginTextField)
|
||||
{
|
||||
[self.emailAndTokenTextField becomeFirstResponder];
|
||||
}
|
||||
else if (textField == self.emailAndTokenTextField)
|
||||
{
|
||||
[self.displayNameTextField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (MXAuthenticationSession*)validateAuthenticationSession:(MXAuthenticationSession*)authSession
|
||||
{
|
||||
// Check whether at least one of the listed flow is supported.
|
||||
BOOL isSupported = NO;
|
||||
|
||||
for (MXLoginFlow *loginFlow in authSession.flows)
|
||||
{
|
||||
// Check whether flow type is defined
|
||||
if ([loginFlow.type isEqualToString:kMXLoginFlowTypeEmailCode])
|
||||
{
|
||||
isSupported = YES;
|
||||
break;
|
||||
}
|
||||
else if (loginFlow.stages.count == 1 && [loginFlow.stages.firstObject isEqualToString:kMXLoginFlowTypeEmailCode])
|
||||
{
|
||||
isSupported = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSupported)
|
||||
{
|
||||
if (authSession.flows.count == 1)
|
||||
{
|
||||
// Return the original session.
|
||||
return authSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep only the supported flow.
|
||||
MXAuthenticationSession *updatedAuthSession = [[MXAuthenticationSession alloc] init];
|
||||
updatedAuthSession.session = authSession.session;
|
||||
updatedAuthSession.params = authSession.params;
|
||||
updatedAuthSession.flows = @[[MXLoginFlow modelFromJSON:@{@"stages":@[kMXLoginFlowTypeEmailCode]}]];
|
||||
return updatedAuthSession;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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" translatesAutoresizingMaskIntoConstraints="NO" id="Nmn-Ry-t32" customClass="MXKAuthInputsEmailCodeBasedView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="122"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="oen-Yq-xJw">
|
||||
<rect key="frame" x="14" y="8" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="gfu-Ef-79U"/>
|
||||
<constraint firstAttribute="width" constant="272" id="y4N-L3-qlh"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Nmn-Ry-t32" id="Ari-Ab-eGE"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="mbS-mV-L86" userLabel="Email-Token">
|
||||
<rect key="frame" x="14" y="46" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="ecT-NT-FqH"/>
|
||||
<constraint firstAttribute="width" constant="272" id="xkx-sr-F8M"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Nmn-Ry-t32" id="yrA-lO-OCi"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please enter your email validation token:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vDa-DE-6Bn">
|
||||
<rect key="frame" x="20" y="8" width="260" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FVM-ec-tja" userLabel="DisplayName">
|
||||
<rect key="frame" x="14" y="84" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="272" id="1b5-Ej-eUB"/>
|
||||
<constraint firstAttribute="height" constant="30" id="bEU-4F-cUv"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Nmn-Ry-t32" id="Q3D-XL-8Ke"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="FVM-ec-tja" firstAttribute="top" secondItem="mbS-mV-L86" secondAttribute="bottom" constant="8" id="0BV-8c-ZVg"/>
|
||||
<constraint firstAttribute="height" constant="122" id="1nP-RR-5GK"/>
|
||||
<constraint firstAttribute="centerX" secondItem="mbS-mV-L86" secondAttribute="centerX" id="2R2-Du-o97"/>
|
||||
<constraint firstItem="oen-Yq-xJw" firstAttribute="top" secondItem="Nmn-Ry-t32" secondAttribute="top" constant="8" id="BkW-Np-qB3"/>
|
||||
<constraint firstItem="mbS-mV-L86" firstAttribute="top" secondItem="oen-Yq-xJw" secondAttribute="bottom" constant="8" id="TRx-gf-Afp"/>
|
||||
<constraint firstAttribute="centerX" secondItem="FVM-ec-tja" secondAttribute="centerX" id="c9K-6H-ufd"/>
|
||||
<constraint firstItem="mbS-mV-L86" firstAttribute="top" secondItem="vDa-DE-6Bn" secondAttribute="bottom" constant="8" id="cq8-qs-59M"/>
|
||||
<constraint firstItem="vDa-DE-6Bn" firstAttribute="top" secondItem="Nmn-Ry-t32" secondAttribute="top" constant="8" id="eAp-Ob-hFr"/>
|
||||
<constraint firstAttribute="centerX" secondItem="vDa-DE-6Bn" secondAttribute="centerX" id="otm-qx-iNL"/>
|
||||
<constraint firstAttribute="centerX" secondItem="oen-Yq-xJw" secondAttribute="centerX" id="s7Y-qf-yrY"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="displayNameTextField" destination="FVM-ec-tja" id="pMF-oP-7Jj"/>
|
||||
<outlet property="emailAndTokenTextField" destination="mbS-mV-L86" id="nY5-gT-wGe"/>
|
||||
<outlet property="promptEmailTokenLabel" destination="vDa-DE-6Bn" id="X3w-5E-0Kg"/>
|
||||
<outlet property="userLoginTextField" destination="oen-Yq-xJw" id="OM2-gy-O9P"/>
|
||||
<outlet property="viewHeightConstraint" destination="1nP-RR-5GK" id="s3W-1L-bWN"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKAuthInputsView.h"
|
||||
|
||||
@interface MXKAuthInputsPasswordBasedView : MXKAuthInputsView
|
||||
|
||||
/**
|
||||
The input text field related to user id or user login.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *userLoginTextField;
|
||||
|
||||
/**
|
||||
The input text field used to fill the password.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *passWordTextField;
|
||||
|
||||
/**
|
||||
The input text field used to fill an email. This item is optional, it is added in case of registration.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
|
||||
|
||||
/**
|
||||
Label used to display email field information.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UILabel *emailInfoLabel;
|
||||
|
||||
/**
|
||||
The text field related to the display name. This item is displayed in case of registration.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UITextField *displayNameTextField;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 "MXKAuthInputsPasswordBasedView.h"
|
||||
|
||||
#import "MXKTools.h"
|
||||
|
||||
#import "MXKAppSettings.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@implementation MXKAuthInputsPasswordBasedView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKAuthInputsPasswordBasedView class])
|
||||
bundle:[NSBundle bundleForClass:[MXKAuthInputsPasswordBasedView class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
_userLoginTextField.placeholder = [MatrixKitL10n loginUserIdPlaceholder];
|
||||
_passWordTextField.placeholder = [MatrixKitL10n loginPasswordPlaceholder];
|
||||
_emailTextField.placeholder = [NSString stringWithFormat:@"%@ (%@)", [MatrixKitL10n loginEmailPlaceholder], [MatrixKitL10n loginOptionalField]];
|
||||
_emailInfoLabel.text = [MatrixKitL10n loginEmailInfo];
|
||||
|
||||
_displayNameTextField.placeholder = [MatrixKitL10n loginDisplayNamePlaceholder];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
||||
{
|
||||
if (type == MXKAuthenticationTypeLogin || type == MXKAuthenticationTypeRegister)
|
||||
{
|
||||
// Validate first the provided session
|
||||
MXAuthenticationSession *validSession = [self validateAuthenticationSession:authSession];
|
||||
|
||||
if ([super setAuthSession:validSession withAuthType:authType])
|
||||
{
|
||||
if (type == MXKAuthenticationTypeLogin)
|
||||
{
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyDone;
|
||||
self.emailTextField.hidden = YES;
|
||||
self.emailInfoLabel.hidden = YES;
|
||||
self.displayNameTextField.hidden = YES;
|
||||
|
||||
self.viewHeightConstraint.constant = self.displayNameTextField.frame.origin.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.passWordTextField.returnKeyType = UIReturnKeyNext;
|
||||
self.emailTextField.hidden = NO;
|
||||
self.emailInfoLabel.hidden = NO;
|
||||
self.displayNameTextField.hidden = NO;
|
||||
|
||||
self.viewHeightConstraint.constant = 179;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString*)validateParameters
|
||||
{
|
||||
NSString *errorMsg = [super validateParameters];
|
||||
|
||||
if (!errorMsg)
|
||||
{
|
||||
// Check user login and pass fields
|
||||
if (!self.areAllRequiredFieldsSet)
|
||||
{
|
||||
errorMsg = [MatrixKitL10n loginInvalidParam];
|
||||
}
|
||||
}
|
||||
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
// Sanity check on required fields
|
||||
if (!self.areAllRequiredFieldsSet)
|
||||
{
|
||||
callback(nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n loginInvalidParam]}]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the user login and check whether it is an email or a username.
|
||||
// TODO: Update the UI view to support the login based on a mobile phone number.
|
||||
NSString *user = self.userLoginTextField.text;
|
||||
user = [user stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
BOOL isEmailAddress = [MXTools isEmailAddress:user];
|
||||
|
||||
NSDictionary *parameters;
|
||||
|
||||
if (isEmailAddress)
|
||||
{
|
||||
parameters = @{
|
||||
@"type": kMXLoginFlowTypePassword,
|
||||
@"identifier": @{
|
||||
@"type": kMXLoginIdentifierTypeThirdParty,
|
||||
@"medium": kMX3PIDMediumEmail,
|
||||
@"address": user
|
||||
},
|
||||
@"password": self.passWordTextField.text
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters = @{
|
||||
@"type": kMXLoginFlowTypePassword,
|
||||
@"identifier": @{
|
||||
@"type": kMXLoginIdentifierTypeUser,
|
||||
@"user": user
|
||||
},
|
||||
@"password": self.passWordTextField.text
|
||||
};
|
||||
}
|
||||
|
||||
callback(parameters, nil);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)areAllRequiredFieldsSet
|
||||
{
|
||||
BOOL ret = [super areAllRequiredFieldsSet];
|
||||
|
||||
// Check user login and pass fields
|
||||
ret = (ret && self.userLoginTextField.text.length && self.passWordTextField.text.length);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
[self.userLoginTextField resignFirstResponder];
|
||||
[self.passWordTextField resignFirstResponder];
|
||||
[self.emailTextField resignFirstResponder];
|
||||
[self.displayNameTextField resignFirstResponder];
|
||||
|
||||
[super dismissKeyboard];
|
||||
}
|
||||
|
||||
- (NSString*)userId
|
||||
{
|
||||
return self.userLoginTextField.text;
|
||||
}
|
||||
|
||||
- (NSString*)password
|
||||
{
|
||||
return self.passWordTextField.text;
|
||||
}
|
||||
|
||||
#pragma mark UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
if (textField.returnKeyType == UIReturnKeyDone)
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[textField resignFirstResponder];
|
||||
|
||||
// Launch authentication now
|
||||
[self.delegate authInputsViewDidPressDoneKey:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
//"Next" key has been pressed
|
||||
if (textField == self.userLoginTextField)
|
||||
{
|
||||
[self.passWordTextField becomeFirstResponder];
|
||||
}
|
||||
else if (textField == self.passWordTextField)
|
||||
{
|
||||
[self.displayNameTextField becomeFirstResponder];
|
||||
}
|
||||
else if (textField == self.displayNameTextField)
|
||||
{
|
||||
[self.emailTextField becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (MXAuthenticationSession*)validateAuthenticationSession:(MXAuthenticationSession*)authSession
|
||||
{
|
||||
// Check whether at least one of the listed flow is supported.
|
||||
BOOL isSupported = NO;
|
||||
|
||||
for (MXLoginFlow *loginFlow in authSession.flows)
|
||||
{
|
||||
// Check whether flow type is defined
|
||||
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword])
|
||||
{
|
||||
isSupported = YES;
|
||||
break;
|
||||
}
|
||||
else if (loginFlow.stages.count == 1 && [loginFlow.stages.firstObject isEqualToString:kMXLoginFlowTypePassword])
|
||||
{
|
||||
isSupported = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSupported)
|
||||
{
|
||||
if (authSession.flows.count == 1)
|
||||
{
|
||||
// Return the original session.
|
||||
return authSession;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep only the supported flow.
|
||||
MXAuthenticationSession *updatedAuthSession = [[MXAuthenticationSession alloc] init];
|
||||
updatedAuthSession.session = authSession.session;
|
||||
updatedAuthSession.params = authSession.params;
|
||||
updatedAuthSession.flows = @[[MXLoginFlow modelFromJSON:@{@"stages":@[kMXLoginFlowTypePassword]}]];
|
||||
return updatedAuthSession;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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" translatesAutoresizingMaskIntoConstraints="NO" id="x74-04-ezp" customClass="MXKAuthInputsPasswordBasedView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="300" height="179"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Matrix ID (e.g. @bob:matrix.org or bob)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="i51-ym-i9T">
|
||||
<rect key="frame" x="14" y="8" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="272" id="IhR-uY-MJZ"/>
|
||||
<constraint firstAttribute="height" constant="30" id="x3j-uC-j0U"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="yCp-R2-dNp"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Password" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="6rs-rR-DkS">
|
||||
<rect key="frame" x="14" y="46" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="Fh2-oZ-UBe"/>
|
||||
<constraint firstAttribute="width" constant="272" id="pCs-je-5Rd"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="t8U-qP-IHU"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Display name (e.g. Bob Obson)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="BQM-LP-8Eq" userLabel="DisplayName">
|
||||
<rect key="frame" x="14" y="84" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="Iwg-IP-ITp"/>
|
||||
<constraint firstAttribute="width" constant="272" id="ZG2-Aw-XkP"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="next" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="qkT-a6-G0Y"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField hidden="YES" opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Email address (optional)" textAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="odF-W1-Vdr" userLabel="Email">
|
||||
<rect key="frame" x="14" y="126" width="272" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="272" id="wZg-GR-5XL"/>
|
||||
<constraint firstAttribute="height" constant="30" id="zVy-8W-zmR"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done" secureTextEntry="YES"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="x74-04-ezp" id="isi-U5-D9A"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="0.0" translatesAutoresizingMaskIntoConstraints="NO" id="OHZ-Ye-nch" userLabel="EmailInfoLabel">
|
||||
<rect key="frame" x="14" y="156" width="272" height="15"/>
|
||||
<string key="text">Specify an email address lets other users find you on Matrix more easily, and will give you a way to reset your password in the future.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="i51-ym-i9T" firstAttribute="top" secondItem="x74-04-ezp" secondAttribute="top" constant="8" id="2sO-Wi-AFi"/>
|
||||
<constraint firstItem="BQM-LP-8Eq" firstAttribute="top" secondItem="6rs-rR-DkS" secondAttribute="bottom" constant="8" id="2vZ-NH-f0H"/>
|
||||
<constraint firstAttribute="centerX" secondItem="odF-W1-Vdr" secondAttribute="centerX" id="BbL-ex-otM"/>
|
||||
<constraint firstItem="OHZ-Ye-nch" firstAttribute="leading" secondItem="x74-04-ezp" secondAttribute="leading" constant="14" id="Ctv-P7-FQl"/>
|
||||
<constraint firstItem="6rs-rR-DkS" firstAttribute="top" secondItem="i51-ym-i9T" secondAttribute="bottom" constant="8" id="EJf-md-dcj"/>
|
||||
<constraint firstAttribute="height" constant="179" id="JbD-Ce-bTY"/>
|
||||
<constraint firstItem="OHZ-Ye-nch" firstAttribute="top" secondItem="odF-W1-Vdr" secondAttribute="bottom" id="LTI-Eq-pH6"/>
|
||||
<constraint firstAttribute="centerX" secondItem="i51-ym-i9T" secondAttribute="centerX" id="WKP-y0-Jon"/>
|
||||
<constraint firstAttribute="centerX" secondItem="BQM-LP-8Eq" secondAttribute="centerX" id="aQQ-kb-tLS"/>
|
||||
<constraint firstAttribute="centerX" secondItem="6rs-rR-DkS" secondAttribute="centerX" id="nIk-ev-cFe"/>
|
||||
<constraint firstItem="odF-W1-Vdr" firstAttribute="top" secondItem="BQM-LP-8Eq" secondAttribute="bottom" constant="12" id="qjk-LC-4NN"/>
|
||||
<constraint firstAttribute="centerX" secondItem="OHZ-Ye-nch" secondAttribute="centerX" id="tvZ-5z-KAm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OHZ-Ye-nch" secondAttribute="trailing" constant="14" id="yQg-6n-P8L"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="displayNameTextField" destination="BQM-LP-8Eq" id="Ptq-yP-WaZ"/>
|
||||
<outlet property="emailInfoLabel" destination="OHZ-Ye-nch" id="fh6-Sz-X5K"/>
|
||||
<outlet property="emailTextField" destination="odF-W1-Vdr" id="DOS-H7-MZy"/>
|
||||
<outlet property="passWordTextField" destination="6rs-rR-DkS" id="VeL-kt-Fpp"/>
|
||||
<outlet property="userLoginTextField" destination="i51-ym-i9T" id="XKi-6m-tFv"/>
|
||||
<outlet property="viewHeightConstraint" destination="JbD-Ce-bTY" id="09n-jN-RM1"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C
|
||||
|
||||
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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
extern NSString *const MXKAuthErrorDomain;
|
||||
|
||||
/**
|
||||
Authentication types
|
||||
*/
|
||||
typedef enum {
|
||||
/**
|
||||
Type used to sign up.
|
||||
*/
|
||||
MXKAuthenticationTypeRegister,
|
||||
/**
|
||||
Type used to sign in.
|
||||
*/
|
||||
MXKAuthenticationTypeLogin,
|
||||
/**
|
||||
Type used to restore an existing account by reseting the password.
|
||||
*/
|
||||
MXKAuthenticationTypeForgotPassword
|
||||
|
||||
} MXKAuthenticationType;
|
||||
|
||||
@class MXKAuthInputsView;
|
||||
|
||||
/**
|
||||
`MXKAuthInputsView` delegate
|
||||
*/
|
||||
@protocol MXKAuthInputsViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that an alert must be presented.
|
||||
|
||||
@param authInputsView the authentication inputs view.
|
||||
@param alert the alert to present.
|
||||
*/
|
||||
- (void)authInputsView:(MXKAuthInputsView*)authInputsView presentAlertController:(UIAlertController*)alert;
|
||||
|
||||
/**
|
||||
For some input fields, the return key of the keyboard is defined as `Done` key.
|
||||
By this method, the delegate is notified when this key is pressed.
|
||||
*/
|
||||
- (void)authInputsViewDidPressDoneKey:(MXKAuthInputsView *)authInputsView;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
The matrix REST Client used to validate third-party identifiers.
|
||||
*/
|
||||
- (MXRestClient *)authInputsViewThirdPartyIdValidationRestClient:(MXKAuthInputsView *)authInputsView;
|
||||
|
||||
/**
|
||||
The identity service used to validate third-party identifiers.
|
||||
*/
|
||||
- (MXIdentityService *)authInputsViewThirdPartyIdValidationIdentityService:(MXKAuthInputsView *)authInputsView;
|
||||
|
||||
/**
|
||||
Tell the delegate to present a view controller modally.
|
||||
|
||||
Note: This method is used to display the countries list during the phone number handling.
|
||||
|
||||
@param authInputsView the authentication inputs view.
|
||||
@param viewControllerToPresent the view controller to present.
|
||||
@param animated YES to animate the presentation.
|
||||
*/
|
||||
- (void)authInputsView:(MXKAuthInputsView *)authInputsView presentViewController:(UIViewController*)viewControllerToPresent animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
Tell the delegate to cancel the current operation.
|
||||
*/
|
||||
- (void)authInputsViewDidCancelOperation:(MXKAuthInputsView *)authInputsView;
|
||||
|
||||
/**
|
||||
Tell the delegate to autodiscover the server configuration.
|
||||
*/
|
||||
- (void)authInputsView:(MXKAuthInputsView *)authInputsView autoDiscoverServerWithDomain:(NSString*)domain;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
`MXKAuthInputsView` is a base class to handle authentication inputs.
|
||||
*/
|
||||
@interface MXKAuthInputsView : MXKView <UITextFieldDelegate>
|
||||
{
|
||||
@protected
|
||||
/**
|
||||
The authentication type (`MXKAuthenticationTypeLogin` by default).
|
||||
*/
|
||||
MXKAuthenticationType type;
|
||||
|
||||
/**
|
||||
The authentication session (nil by default).
|
||||
*/
|
||||
MXAuthenticationSession *currentSession;
|
||||
|
||||
/**
|
||||
Alert used to display inputs error.
|
||||
*/
|
||||
UIAlertController *inputsAlert;
|
||||
}
|
||||
|
||||
/**
|
||||
The view delegate.
|
||||
*/
|
||||
@property (nonatomic, weak) id <MXKAuthInputsViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
The current authentication type (`MXKAuthenticationTypeLogin` by default).
|
||||
*/
|
||||
@property (nonatomic, readonly) MXKAuthenticationType authType;
|
||||
|
||||
/**
|
||||
The current authentication session if any.
|
||||
*/
|
||||
@property (nonatomic, readonly) MXAuthenticationSession *authSession;
|
||||
|
||||
/**
|
||||
The current filled user identifier (nil by default).
|
||||
*/
|
||||
@property (nonatomic, readonly) NSString *userId;
|
||||
|
||||
/**
|
||||
The current filled password (nil by default).
|
||||
*/
|
||||
@property (nonatomic, readonly) NSString *password;
|
||||
|
||||
/**
|
||||
The layout constraint defined on the view height. This height takes into account shown/hidden fields.
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *viewHeightConstraint;
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for the auth inputs view.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during
|
||||
initialization or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Creates and returns a new `MXKAuthInputsView` object.
|
||||
|
||||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
|
||||
@return An initialized `MXKAuthInputsView` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)authInputsView;
|
||||
|
||||
/**
|
||||
Finalize the authentication inputs view with a session and a type.
|
||||
Use this method to restore the view in its initial step.
|
||||
|
||||
@discussion You may override this method to check/update the flows listed in the provided authentication session.
|
||||
|
||||
@param authSession the authentication session returned by the homeserver.
|
||||
@param authType the authentication type (see 'MXKAuthenticationType').
|
||||
@return YES if the provided session and type are supported by the MXKAuthInputsView-inherited class. Note the unsupported flows should be here removed from the stored authentication session (see the resulting session in the property named 'authSession').
|
||||
*/
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType;
|
||||
|
||||
/**
|
||||
Check the validity of the required parameters.
|
||||
|
||||
@return an error message in case of wrong parameters (nil by default).
|
||||
*/
|
||||
- (NSString*)validateParameters;
|
||||
|
||||
/**
|
||||
Prepare the set of the inputs in order to launch an authentication process.
|
||||
|
||||
@param callback the block called when the parameters are prepared. The resulting parameter dictionary is nil
|
||||
if something fails (for example when a parameter or a required input is missing). The failure reason is provided in error parameter (nil by default).
|
||||
*/
|
||||
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback;
|
||||
|
||||
/**
|
||||
Update the current authentication session by providing the list of successful stages.
|
||||
|
||||
@param completedStages the list of stages the client has completed successfully. This is an array of MXLoginFlowType.
|
||||
@param callback the block called when the parameters have been updated for the next stage. The resulting parameter dictionary is nil
|
||||
if something fails (for example when a parameter or a required input is missing). The failure reason is provided in error parameter (nil by default).
|
||||
*/
|
||||
- (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdateParameters:(void (^)(NSDictionary *parameters, NSError *error))callback;
|
||||
|
||||
/**
|
||||
Update the current authentication session by providing a set of registration parameters.
|
||||
|
||||
@discussion This operation failed if the current authentication type is MXKAuthenticationTypeLogin.
|
||||
|
||||
@param registrationParameters a set of parameters to use during the current registration process.
|
||||
@return YES if the provided set of parameters is supported.
|
||||
*/
|
||||
- (BOOL)setExternalRegistrationParameters:(NSDictionary *)registrationParameters;
|
||||
|
||||
/**
|
||||
Update the current authentication session by providing soft logout credentials.
|
||||
*/
|
||||
@property (nonatomic) MXCredentials *softLogoutCredentials;
|
||||
|
||||
/**
|
||||
Tell whether all required fields are set
|
||||
*/
|
||||
- (BOOL)areAllRequiredFieldsSet;
|
||||
|
||||
/**
|
||||
Force dismiss keyboard
|
||||
*/
|
||||
- (void)dismissKeyboard;
|
||||
|
||||
/**
|
||||
Switch in next authentication flow step by updating the layout.
|
||||
|
||||
@discussion This method is supposed to be called only if the current operation succeeds.
|
||||
*/
|
||||
- (void)nextStep;
|
||||
|
||||
/**
|
||||
Dispose any resources and listener.
|
||||
*/
|
||||
- (void)destroy;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 "MXKAuthInputsView.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
NSString *const MXKAuthErrorDomain = @"MXKAuthErrorDomain";
|
||||
|
||||
@implementation MXKAuthInputsView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// By default, no nib is available.
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (instancetype)authInputsView
|
||||
{
|
||||
// Check whether a xib is defined
|
||||
if ([[self class] nib])
|
||||
{
|
||||
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
}
|
||||
|
||||
return [[[self class] alloc] init];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
type = MXKAuthenticationTypeLogin;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
type = MXKAuthenticationTypeLogin;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)setAuthSession:(MXAuthenticationSession *)authSession withAuthType:(MXKAuthenticationType)authType
|
||||
{
|
||||
if (authSession)
|
||||
{
|
||||
type = authType;
|
||||
currentSession = authSession;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)validateParameters
|
||||
{
|
||||
// Currently no field to check here
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)prepareParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
||||
{
|
||||
// Do nothing by default
|
||||
if (callback)
|
||||
{
|
||||
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n notSupportedYet]}]);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdateParameters:(void (^)(NSDictionary *parameters, NSError *error))callback
|
||||
{
|
||||
// Do nothing by default
|
||||
if (callback)
|
||||
{
|
||||
callback (nil, [NSError errorWithDomain:MXKAuthErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey:[MatrixKitL10n notSupportedYet]}]);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setExternalRegistrationParameters:(NSDictionary *)registrationParameters
|
||||
{
|
||||
// Not supported by default
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)areAllRequiredFieldsSet
|
||||
{
|
||||
// Currently no field to check here
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)nextStep
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
if (inputsAlert)
|
||||
{
|
||||
[inputsAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
inputsAlert = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (MXKAuthenticationType)authType
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
- (MXAuthenticationSession*)authSession
|
||||
{
|
||||
return currentSession;
|
||||
}
|
||||
|
||||
- (NSString*)userId
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString*)password
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 <MatrixSDK/MatrixSDK.h>
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface MXKAuthenticationFallbackWebView : WKWebView <WKNavigationDelegate>
|
||||
|
||||
/**
|
||||
Open authentication fallback page into the webview.
|
||||
|
||||
@param fallbackPage the fallback page hosted by a homeserver.
|
||||
@param success the block called when the user has been successfully logged in or registered.
|
||||
*/
|
||||
- (void)openFallbackPage:(NSString*)fallbackPage success:(void (^)(MXLoginResponse *loginResponse))success;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKAuthenticationFallbackWebView.h"
|
||||
|
||||
// Generic method to make a bridge between JS and the WKWebView
|
||||
NSString *kMXKJavascriptSendObjectMessage = @"window.sendObjectMessage = function(parameters) { \
|
||||
var iframe = document.createElement('iframe'); \
|
||||
iframe.setAttribute('src', 'js:' + JSON.stringify(parameters)); \
|
||||
\
|
||||
document.documentElement.appendChild(iframe); \
|
||||
iframe.parentNode.removeChild(iframe); \
|
||||
iframe = null; \
|
||||
};";
|
||||
|
||||
// The function the fallback page calls when the registration is complete
|
||||
NSString *kMXKJavascriptOnRegistered = @"window.matrixRegistration.onRegistered = function(homeserverUrl, userId, accessToken) { \
|
||||
sendObjectMessage({ \
|
||||
'action': 'onRegistered', \
|
||||
'homeServer': homeserverUrl, \
|
||||
'userId': userId, \
|
||||
'accessToken': accessToken \
|
||||
}); \
|
||||
};";
|
||||
|
||||
// The function the fallback page calls when the login is complete
|
||||
NSString *kMXKJavascriptOnLogin = @"window.matrixLogin.onLogin = function(response) { \
|
||||
sendObjectMessage({ \
|
||||
'action': 'onLogin', \
|
||||
'response': response \
|
||||
}); \
|
||||
};";
|
||||
|
||||
@interface MXKAuthenticationFallbackWebView ()
|
||||
{
|
||||
// The block called when the login or the registration is successful
|
||||
void (^onSuccess)(MXLoginResponse *);
|
||||
|
||||
// Activity indicator
|
||||
UIActivityIndicatorView *activityIndicator;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKAuthenticationFallbackWebView
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (activityIndicator)
|
||||
{
|
||||
[activityIndicator removeFromSuperview];
|
||||
activityIndicator = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)openFallbackPage:(NSString *)fallbackPage success:(void (^)(MXLoginResponse *))success
|
||||
{
|
||||
self.navigationDelegate = self;
|
||||
|
||||
onSuccess = success;
|
||||
|
||||
// Add activity indicator
|
||||
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
||||
activityIndicator.center = self.center;
|
||||
[self addSubview:activityIndicator];
|
||||
[activityIndicator startAnimating];
|
||||
|
||||
// Delete cookies to launch login process from scratch
|
||||
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies])
|
||||
{
|
||||
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
|
||||
}
|
||||
|
||||
[self loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:fallbackPage]]];
|
||||
}
|
||||
|
||||
#pragma mark - WKNavigationDelegate
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
if (activityIndicator)
|
||||
{
|
||||
[activityIndicator stopAnimating];
|
||||
[activityIndicator removeFromSuperview];
|
||||
activityIndicator = nil;
|
||||
}
|
||||
|
||||
[self evaluateJavaScript:kMXKJavascriptSendObjectMessage completionHandler:^(id _Nullable response, NSError * _Nullable error) {
|
||||
|
||||
}];
|
||||
[self evaluateJavaScript:kMXKJavascriptOnRegistered completionHandler:^(id _Nullable response, NSError * _Nullable error) {
|
||||
|
||||
}];
|
||||
[self evaluateJavaScript:kMXKJavascriptOnLogin completionHandler:^(id _Nullable response, NSError * _Nullable error) {
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
||||
{
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] decidePolicyForNavigationAction");
|
||||
|
||||
NSString *urlString = navigationAction.request.URL.absoluteString;
|
||||
|
||||
if ([urlString hasPrefix:@"js:"])
|
||||
{
|
||||
// do not log urlString, it may have an access token
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] URL has js: prefix");
|
||||
|
||||
// Listen only to scheme of the JS-WKWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
|
||||
error:&error];
|
||||
|
||||
if (error)
|
||||
{
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Error when parsing json: %@", error);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([@"onRegistered" isEqualToString:parameters[@"action"]])
|
||||
{
|
||||
// Translate the JS registration event to MXLoginResponse
|
||||
// We cannot use [MXLoginResponse modelFromJSON:] because of https://github.com/matrix-org/synapse/issues/4756
|
||||
// Because of this issue, we cannot get the device_id allocated by the homeserver
|
||||
// TODO: Fix it once the homeserver issue is fixed (filed at https://github.com/vector-im/riot-meta/issues/273).
|
||||
MXLoginResponse *loginResponse = [MXLoginResponse new];
|
||||
loginResponse.homeserver = parameters[@"homeServer"];
|
||||
loginResponse.userId = parameters[@"userId"];
|
||||
loginResponse.accessToken = parameters[@"accessToken"];
|
||||
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Registered on homeserver: %@", loginResponse.homeserver);
|
||||
|
||||
// Sanity check
|
||||
if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
|
||||
{
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
|
||||
// And inform the client
|
||||
onSuccess(loginResponse);
|
||||
}
|
||||
}
|
||||
else if ([@"onLogin" isEqualToString:parameters[@"action"]])
|
||||
{
|
||||
// Translate the JS login event to MXLoginResponse
|
||||
MXLoginResponse *loginResponse;
|
||||
MXJSONModelSetMXJSONModel(loginResponse, MXLoginResponse, parameters[@"response"]);
|
||||
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Logged in on homeserver: %@", loginResponse.homeserver);
|
||||
|
||||
// Sanity check
|
||||
if (loginResponse.homeserver.length && loginResponse.userId.length && loginResponse.accessToken.length)
|
||||
{
|
||||
MXLogDebug(@"[MXKAuthenticationFallbackWebView] Call success block");
|
||||
// And inform the client
|
||||
onSuccess(loginResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
return;
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicyAllow);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import <WebKit/WebKit.h>
|
||||
|
||||
@interface MXKAuthenticationRecaptchaWebView : WKWebView
|
||||
|
||||
/**
|
||||
Open reCAPTCHA widget into a webview.
|
||||
|
||||
@param siteKey the site key.
|
||||
@param homeServer the homeserver URL.
|
||||
@param callback the block called when the user has received reCAPTCHA response.
|
||||
*/
|
||||
- (void)openRecaptchaWidgetWithSiteKey:(NSString*)siteKey fromHomeServer:(NSString*)homeServer callback:(void (^)(NSString *response))callback;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2018 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 "MXKAuthenticationRecaptchaWebView.h"
|
||||
|
||||
NSString *kMXKRecaptchaHTMLString = @"<html> \
|
||||
<head> \
|
||||
<meta name='viewport' content='initial-scale=1.0' /> \
|
||||
<script type=\"text/javascript\"> \
|
||||
var verifyCallback = function(response) { \
|
||||
/* Generic method to make a bridge between JS and the WKWebView*/ \
|
||||
var iframe = document.createElement('iframe'); \
|
||||
iframe.setAttribute('src', 'js:' + JSON.stringify({'action': 'verifyCallback', 'response': response})); \
|
||||
\
|
||||
document.documentElement.appendChild(iframe); \
|
||||
iframe.parentNode.removeChild(iframe); \
|
||||
iframe = null; \
|
||||
}; \
|
||||
var onloadCallback = function() { \
|
||||
grecaptcha.render('recaptcha_widget', { \
|
||||
'sitekey' : '%@', \
|
||||
'callback': verifyCallback \
|
||||
}); \
|
||||
}; \
|
||||
</script> \
|
||||
</head> \
|
||||
<body> \
|
||||
<div id=\"recaptcha_widget\"></div> \
|
||||
<script src=\"https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit\" async defer> \
|
||||
</script> \
|
||||
</body> \
|
||||
</html>";
|
||||
|
||||
@interface MXKAuthenticationRecaptchaWebView () <WKNavigationDelegate>
|
||||
{
|
||||
// The block called when the reCAPTCHA response is received
|
||||
void (^onResponse)(NSString *);
|
||||
|
||||
// Activity indicator
|
||||
UIActivityIndicatorView *activityIndicator;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKAuthenticationRecaptchaWebView
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (activityIndicator)
|
||||
{
|
||||
[activityIndicator removeFromSuperview];
|
||||
activityIndicator = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)openRecaptchaWidgetWithSiteKey:(NSString*)siteKey fromHomeServer:(NSString*)homeServer callback:(void (^)(NSString *response))callback
|
||||
{
|
||||
self.navigationDelegate = self;
|
||||
|
||||
onResponse = callback;
|
||||
|
||||
// Add activity indicator
|
||||
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
||||
activityIndicator.center = self.center;
|
||||
[self addSubview:activityIndicator];
|
||||
[activityIndicator startAnimating];
|
||||
|
||||
NSString *htmlString = [NSString stringWithFormat:kMXKRecaptchaHTMLString, siteKey];
|
||||
|
||||
[self loadHTMLString:htmlString baseURL:[NSURL URLWithString:homeServer]];
|
||||
}
|
||||
|
||||
#pragma mark - WKNavigationDelegate
|
||||
|
||||
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
if (activityIndicator)
|
||||
{
|
||||
[activityIndicator stopAnimating];
|
||||
[activityIndicator removeFromSuperview];
|
||||
activityIndicator = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
||||
{
|
||||
NSString *urlString = navigationAction.request.URL.absoluteString;
|
||||
|
||||
if ([urlString hasPrefix:@"js:"])
|
||||
{
|
||||
// Listen only to scheme of the JS-WKWebView bridge
|
||||
NSString *jsonString = [[[urlString componentsSeparatedByString:@"js:"] lastObject] stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
|
||||
NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parameters = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers
|
||||
error:&error];
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if ([@"verifyCallback" isEqualToString:parameters[@"action"]])
|
||||
{
|
||||
// Transfer the reCAPTCHA response
|
||||
onResponse(parameters[@"response"]);
|
||||
}
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
return;
|
||||
}
|
||||
decisionHandler(WKNavigationActionPolicyAllow);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#pragma mark - Imports
|
||||
|
||||
@import Foundation;
|
||||
@import UIKit;
|
||||
|
||||
#pragma mark - Types
|
||||
|
||||
typedef void (^MXKBarButtonItemAction)(void);
|
||||
|
||||
#pragma mark - Interface
|
||||
|
||||
/**
|
||||
`MXKBarButtonItem` is a subclass of UIBarButtonItem allowing to use convenient action block instead of action selector.
|
||||
*/
|
||||
@interface MXKBarButtonItem : UIBarButtonItem
|
||||
|
||||
#pragma mark - Instance Methods
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action;
|
||||
- (instancetype)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#pragma mark - Imports
|
||||
|
||||
#import "MXKBarButtonItem.h"
|
||||
|
||||
#pragma mark - Private Interface
|
||||
|
||||
@interface MXKBarButtonItem ()
|
||||
|
||||
#pragma mark - Private Properties
|
||||
|
||||
@property (nonatomic, copy) MXKBarButtonItemAction actionBlock;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Implementation
|
||||
|
||||
@implementation MXKBarButtonItem
|
||||
|
||||
#pragma mark - Public methods
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action
|
||||
{
|
||||
self = [self initWithImage:image style:style target:self action:@selector(executeAction:)];
|
||||
if (self)
|
||||
{
|
||||
self.actionBlock = action;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style action:(MXKBarButtonItemAction)action
|
||||
{
|
||||
self = [self initWithTitle:title style:style target:self action:@selector(executeAction:)];
|
||||
if (self)
|
||||
{
|
||||
self.actionBlock = action;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Private methods
|
||||
|
||||
- (void)executeAction:(id)sender
|
||||
{
|
||||
if (self.actionBlock)
|
||||
{
|
||||
self.actionBlock();
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
#import "MXKCellRendering.h"
|
||||
#import "MXKImageView.h"
|
||||
|
||||
/**
|
||||
List the accessory view types for a 'MXKContactTableCell' instance.
|
||||
*/
|
||||
typedef enum : NSUInteger {
|
||||
/**
|
||||
Don't show accessory view by default.
|
||||
*/
|
||||
MXKContactTableCellAccessoryCustom,
|
||||
/**
|
||||
The accessory view is automatically handled. It shown only for contact with matrix identifier(s).
|
||||
*/
|
||||
MXKContactTableCellAccessoryMatrixIcon
|
||||
|
||||
} MXKContactTableCellAccessoryType;
|
||||
|
||||
|
||||
#pragma mark - MXKCellRenderingDelegate cell tap locations
|
||||
|
||||
/**
|
||||
Action identifier used when the user tapped on contact thumbnail view.
|
||||
|
||||
The `userInfo` dictionary contains an `NSString` object under the `kMXKContactCellContactIdKey` key, representing the contact id of the tapped avatar.
|
||||
*/
|
||||
extern NSString *const kMXKContactCellTapOnThumbnailView;
|
||||
|
||||
/**
|
||||
Notifications `userInfo` keys
|
||||
*/
|
||||
extern NSString *const kMXKContactCellContactIdKey;
|
||||
|
||||
/**
|
||||
'MXKContactTableCell' is a base class for displaying a contact.
|
||||
*/
|
||||
@interface MXKContactTableCell : MXKTableViewCell <MXKCellRendering>
|
||||
|
||||
@property (strong, nonatomic) IBOutlet MXKImageView *thumbnailView;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *contactDisplayNameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *matrixDisplayNameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *matrixIDLabel;
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIView *contactAccessoryView;
|
||||
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *contactAccessoryViewHeightConstraint;
|
||||
@property (unsafe_unretained, nonatomic) IBOutlet NSLayoutConstraint *contactAccessoryViewWidthConstraint;
|
||||
@property (strong, nonatomic) IBOutlet UIImageView *contactAccessoryImageView;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *contactAccessoryButton;
|
||||
|
||||
/**
|
||||
The default picture displayed when no picture is available.
|
||||
*/
|
||||
@property (nonatomic) UIImage *picturePlaceholder;
|
||||
|
||||
/**
|
||||
The thumbnail display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
|
||||
*/
|
||||
@property (nonatomic) MXKTableViewCellDisplayBoxType thumbnailDisplayBoxType;
|
||||
|
||||
/**
|
||||
The accessory view type ('MXKContactTableCellAccessoryCustom' by default)
|
||||
*/
|
||||
@property (nonatomic) MXKContactTableCellAccessoryType contactAccessoryViewType;
|
||||
|
||||
/**
|
||||
Tell whether the matrix presence of the contact is displayed or not (NO by default)
|
||||
*/
|
||||
@property (nonatomic) BOOL hideMatrixPresence;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 "MXKContactTableCell.h"
|
||||
|
||||
@import MatrixSDK.MXTools;
|
||||
|
||||
#import "MXKContactManager.h"
|
||||
#import "MXKAppSettings.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#pragma mark - Constant definitions
|
||||
NSString *const kMXKContactCellTapOnThumbnailView = @"kMXKContactCellTapOnThumbnailView";
|
||||
|
||||
NSString *const kMXKContactCellContactIdKey = @"kMXKContactCellContactIdKey";
|
||||
|
||||
@interface MXKContactTableCell()
|
||||
{
|
||||
/**
|
||||
The current displayed contact.
|
||||
*/
|
||||
MXKContact *contact;
|
||||
|
||||
/**
|
||||
The observer of the presence for matrix user.
|
||||
*/
|
||||
id mxPresenceObserver;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKContactTableCell
|
||||
@synthesize delegate;
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
self.thumbnailDisplayBoxType = MXKTableViewCellDisplayBoxTypeDefault;
|
||||
|
||||
// No accessory view by default
|
||||
self.contactAccessoryViewType = MXKContactTableCellAccessoryCustom;
|
||||
|
||||
self.hideMatrixPresence = NO;
|
||||
}
|
||||
|
||||
- (void)customizeTableViewCellRendering
|
||||
{
|
||||
[super customizeTableViewCellRendering];
|
||||
|
||||
self.thumbnailView.defaultBackgroundColor = [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (self.thumbnailDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
|
||||
{
|
||||
// Round image view for thumbnail
|
||||
self.thumbnailView.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
|
||||
self.thumbnailView.clipsToBounds = YES;
|
||||
}
|
||||
else if (self.thumbnailDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
|
||||
{
|
||||
self.thumbnailView.layer.cornerRadius = 5;
|
||||
self.thumbnailView.clipsToBounds = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.thumbnailView.layer.cornerRadius = 0;
|
||||
self.thumbnailView.clipsToBounds = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage*)picturePlaceholder
|
||||
{
|
||||
return [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"default-profile"];
|
||||
}
|
||||
|
||||
- (void)setContactAccessoryViewType:(MXKContactTableCellAccessoryType)contactAccessoryViewType
|
||||
{
|
||||
_contactAccessoryViewType = contactAccessoryViewType;
|
||||
|
||||
if (contactAccessoryViewType == MXKContactTableCellAccessoryMatrixIcon)
|
||||
{
|
||||
// Load default matrix icon
|
||||
self.contactAccessoryImageView.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"matrixUser"];
|
||||
self.contactAccessoryImageView.hidden = NO;
|
||||
self.contactAccessoryButton.hidden = YES;
|
||||
|
||||
// Update accessory view visibility
|
||||
[self refreshMatrixIdentifiers];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide accessory view by default
|
||||
self.contactAccessoryView.hidden = YES;
|
||||
self.contactAccessoryImageView.hidden = YES;
|
||||
self.contactAccessoryButton.hidden = YES;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - MXKCellRendering
|
||||
|
||||
- (void)render:(MXKCellData *)cellData
|
||||
{
|
||||
// Sanity check: accept only object of MXKContact classes or sub-classes
|
||||
NSParameterAssert([cellData isKindOfClass:[MXKContact class]]);
|
||||
|
||||
contact = (MXKContact*)cellData;
|
||||
|
||||
// remove any pending observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
if (mxPresenceObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
|
||||
mxPresenceObserver = nil;
|
||||
}
|
||||
|
||||
self.thumbnailView.layer.borderWidth = 0;
|
||||
|
||||
if (contact)
|
||||
{
|
||||
// Be warned when the thumbnail is updated
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil];
|
||||
|
||||
if (! self.hideMatrixPresence)
|
||||
{
|
||||
// Observe contact presence change
|
||||
mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
// get the matrix identifiers
|
||||
NSArray* matrixIdentifiers = self->contact.matrixIdentifiers;
|
||||
if (matrixIdentifiers.count > 0)
|
||||
{
|
||||
// Consider only the first id
|
||||
NSString *matrixUserID = matrixIdentifiers.firstObject;
|
||||
if ([matrixUserID isEqualToString:notif.object])
|
||||
{
|
||||
[self refreshPresenceUserRing:[MXTools presence:[notif.userInfo objectForKey:kMXKContactManagerMatrixPresenceKey]]];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
if (!contact.isMatrixContact)
|
||||
{
|
||||
// Be warned when the linked matrix IDs are updated
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMatrixIdUpdate:) name:kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification object:nil];
|
||||
}
|
||||
|
||||
NSArray* matrixIDs = contact.matrixIdentifiers;
|
||||
|
||||
if (matrixIDs.count)
|
||||
{
|
||||
self.contactDisplayNameLabel.hidden = YES;
|
||||
|
||||
self.matrixDisplayNameLabel.hidden = NO;
|
||||
self.matrixDisplayNameLabel.text = contact.displayName;
|
||||
self.matrixIDLabel.hidden = NO;
|
||||
self.matrixIDLabel.text = [matrixIDs firstObject];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.contactDisplayNameLabel.hidden = NO;
|
||||
self.contactDisplayNameLabel.text = contact.displayName;
|
||||
|
||||
self.matrixDisplayNameLabel.hidden = YES;
|
||||
self.matrixIDLabel.hidden = YES;
|
||||
}
|
||||
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onContactThumbnailTap:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[self.thumbnailView addGestureRecognizer:tap];
|
||||
}
|
||||
|
||||
[self refreshContactThumbnail];
|
||||
[self manageMatrixIcon];
|
||||
}
|
||||
|
||||
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth
|
||||
{
|
||||
// The height is fixed
|
||||
return 50;
|
||||
}
|
||||
|
||||
- (void)didEndDisplay
|
||||
{
|
||||
// remove any pending observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
if (mxPresenceObserver) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:mxPresenceObserver];
|
||||
mxPresenceObserver = nil;
|
||||
}
|
||||
|
||||
// Remove all gesture recognizer
|
||||
while (self.thumbnailView.gestureRecognizers.count)
|
||||
{
|
||||
[self.thumbnailView removeGestureRecognizer:self.thumbnailView.gestureRecognizers[0]];
|
||||
}
|
||||
|
||||
self.delegate = nil;
|
||||
contact = nil;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)refreshMatrixIdentifiers
|
||||
{
|
||||
// Look for a potential matrix user linked with this contact
|
||||
NSArray* matrixIdentifiers = contact.matrixIdentifiers;
|
||||
|
||||
if ((matrixIdentifiers.count > 0) && (! self.hideMatrixPresence))
|
||||
{
|
||||
// Consider only the first matrix identifier
|
||||
NSString* matrixUserID = matrixIdentifiers.firstObject;
|
||||
|
||||
// Consider here all sessions reported into contact manager
|
||||
NSArray* mxSessions = [MXKContactManager sharedManager].mxSessions;
|
||||
for (MXSession *mxSession in mxSessions)
|
||||
{
|
||||
MXUser *mxUser = [mxSession userWithUserId:matrixUserID];
|
||||
if (mxUser)
|
||||
{
|
||||
[self refreshPresenceUserRing:mxUser.presence];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update accessory view visibility
|
||||
if (self.contactAccessoryViewType == MXKContactTableCellAccessoryMatrixIcon)
|
||||
{
|
||||
self.contactAccessoryView.hidden = (!matrixIdentifiers.count);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshContactThumbnail
|
||||
{
|
||||
self.thumbnailView.image = [contact thumbnailWithPreferedSize:self.thumbnailView.frame.size];
|
||||
|
||||
if (!self.thumbnailView.image)
|
||||
{
|
||||
self.thumbnailView.image = self.picturePlaceholder;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshPresenceUserRing:(MXPresence)presenceStatus
|
||||
{
|
||||
UIColor* ringColor;
|
||||
|
||||
switch (presenceStatus)
|
||||
{
|
||||
case MXPresenceOnline:
|
||||
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForOnlineUser];
|
||||
break;
|
||||
case MXPresenceUnavailable:
|
||||
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForUnavailableUser];
|
||||
break;
|
||||
case MXPresenceOffline:
|
||||
ringColor = [[MXKAppSettings standardAppSettings] presenceColorForOfflineUser];
|
||||
break;
|
||||
default:
|
||||
ringColor = nil;
|
||||
}
|
||||
|
||||
// if the thumbnail is defined
|
||||
if (ringColor && (! self.hideMatrixPresence))
|
||||
{
|
||||
self.thumbnailView.layer.borderWidth = 2;
|
||||
self.thumbnailView.layer.borderColor = ringColor.CGColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
// remove the border
|
||||
// else it draws black border
|
||||
self.thumbnailView.layer.borderWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)manageMatrixIcon
|
||||
{
|
||||
// try to update the thumbnail with the matrix thumbnail
|
||||
if (contact.matrixIdentifiers)
|
||||
{
|
||||
[self refreshContactThumbnail];
|
||||
}
|
||||
|
||||
[self refreshMatrixIdentifiers];
|
||||
}
|
||||
|
||||
- (void)onMatrixIdUpdate:(NSNotification *)notif
|
||||
{
|
||||
// sanity check
|
||||
if ([notif.object isKindOfClass:[NSString class]])
|
||||
{
|
||||
NSString* contactID = notif.object;
|
||||
|
||||
if ([contactID isEqualToString:contact.contactID])
|
||||
{
|
||||
[self manageMatrixIcon];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onThumbnailUpdate:(NSNotification *)notif
|
||||
{
|
||||
// sanity check
|
||||
if ([notif.object isKindOfClass:[NSString class]])
|
||||
{
|
||||
NSString* contactID = notif.object;
|
||||
|
||||
if ([contactID isEqualToString:contact.contactID])
|
||||
{
|
||||
[self refreshContactThumbnail];
|
||||
|
||||
[self refreshMatrixIdentifiers];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Action
|
||||
|
||||
- (IBAction)onContactThumbnailTap:(id)sender
|
||||
{
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate cell:self didRecognizeAction:kMXKContactCellTapOnThumbnailView userInfo:@{kMXKContactCellContactIdKey: contact.contactID}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="ChX-Tl-3Zt" customClass="MXKContactTableCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ChX-Tl-3Zt" id="SeD-4h-8Bk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="49"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="2UL-NX-Nd1" userLabel="Contact Image View" customClass="MXKImageView">
|
||||
<rect key="frame" x="8" y="5" width="40" height="40"/>
|
||||
<color key="backgroundColor" red="0.89720267057418823" green="0.89720267057418823" blue="0.89720267057418823" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<gestureRecognizers/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="40" id="QUm-Hf-dkn"/>
|
||||
<constraint firstAttribute="width" constant="40" id="z2e-H8-DcL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Single line" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AqS-OH-Gcq">
|
||||
<rect key="frame" x="60" y="10" width="510" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="30" id="zWe-gm-gkP"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Line 1" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MBc-s3-6mn">
|
||||
<rect key="frame" x="60" y="8" width="510" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="22" id="7wT-nd-kfp"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Line 2" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dqg-OY-Nhb" userLabel="Line 2">
|
||||
<rect key="frame" x="60" y="28" width="510" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="BEu-8L-a1q"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.33333333333333331" green="0.33333333333333331" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uMV-DW-X26" userLabel="Accessory View">
|
||||
<rect key="frame" x="574" y="17" width="16" height="16"/>
|
||||
<subviews>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="BcH-3j-P0i" userLabel="Accessory Image View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="16" height="16"/>
|
||||
</imageView>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="j2S-s7-q50" userLabel="Accessory Button">
|
||||
<rect key="frame" x="-7" y="-7" width="30" height="30"/>
|
||||
<state key="normal">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="j2S-s7-q50" secondAttribute="centerY" id="3Qb-sa-m7f"/>
|
||||
<constraint firstAttribute="bottom" secondItem="BcH-3j-P0i" secondAttribute="bottom" id="KIl-sC-FVk"/>
|
||||
<constraint firstAttribute="centerX" secondItem="j2S-s7-q50" secondAttribute="centerX" id="Wp1-CK-YKk"/>
|
||||
<constraint firstAttribute="width" constant="16" id="cWZ-C3-LfR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="BcH-3j-P0i" secondAttribute="trailing" id="mol-ud-P3Q"/>
|
||||
<constraint firstItem="BcH-3j-P0i" firstAttribute="top" secondItem="uMV-DW-X26" secondAttribute="top" id="sT8-GB-Hzx"/>
|
||||
<constraint firstItem="BcH-3j-P0i" firstAttribute="leading" secondItem="uMV-DW-X26" secondAttribute="leading" id="wTq-hz-2Wl"/>
|
||||
<constraint firstAttribute="height" constant="16" id="xAv-oq-PkL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="2UL-NX-Nd1" secondAttribute="centerY" id="2z5-cx-W9f"/>
|
||||
<constraint firstItem="Dqg-OY-Nhb" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="5I6-PQ-4Bt"/>
|
||||
<constraint firstAttribute="centerY" secondItem="AqS-OH-Gcq" secondAttribute="centerY" id="9jE-l2-2tm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="uMV-DW-X26" secondAttribute="trailing" constant="10" id="DDZ-6z-ppJ"/>
|
||||
<constraint firstAttribute="centerY" secondItem="uMV-DW-X26" secondAttribute="centerY" id="OFV-RA-kUY"/>
|
||||
<constraint firstItem="2UL-NX-Nd1" firstAttribute="leading" secondItem="SeD-4h-8Bk" secondAttribute="leading" constant="8" id="Tno-FP-CJJ"/>
|
||||
<constraint firstItem="MBc-s3-6mn" firstAttribute="width" secondItem="Dqg-OY-Nhb" secondAttribute="width" id="Wk2-qO-Gdj"/>
|
||||
<constraint firstItem="AqS-OH-Gcq" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="dSV-cR-GP0"/>
|
||||
<constraint firstItem="MBc-s3-6mn" firstAttribute="leading" secondItem="2UL-NX-Nd1" secondAttribute="trailing" constant="12" id="f7x-gz-aR7"/>
|
||||
<constraint firstItem="uMV-DW-X26" firstAttribute="leading" secondItem="Dqg-OY-Nhb" secondAttribute="trailing" constant="4" id="oJV-sA-GzJ"/>
|
||||
<constraint firstItem="AqS-OH-Gcq" firstAttribute="width" secondItem="MBc-s3-6mn" secondAttribute="width" id="qGv-M1-qC6"/>
|
||||
<constraint firstItem="MBc-s3-6mn" firstAttribute="top" secondItem="SeD-4h-8Bk" secondAttribute="topMargin" id="qf2-UP-3lW"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="Dqg-OY-Nhb" secondAttribute="bottom" constant="-5" id="tLG-uu-oPr"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="contactAccessoryButton" destination="j2S-s7-q50" id="9hP-Zr-kHK"/>
|
||||
<outlet property="contactAccessoryImageView" destination="BcH-3j-P0i" id="Omi-4G-fbt"/>
|
||||
<outlet property="contactAccessoryView" destination="uMV-DW-X26" id="6kX-sp-tuz"/>
|
||||
<outlet property="contactAccessoryViewHeightConstraint" destination="xAv-oq-PkL" id="MGt-PN-Brr"/>
|
||||
<outlet property="contactAccessoryViewWidthConstraint" destination="cWZ-C3-LfR" id="0pP-6i-gop"/>
|
||||
<outlet property="contactDisplayNameLabel" destination="AqS-OH-Gcq" id="Znz-dt-zKJ"/>
|
||||
<outlet property="matrixDisplayNameLabel" destination="MBc-s3-6mn" id="BWe-zO-aM3"/>
|
||||
<outlet property="matrixIDLabel" destination="Dqg-OY-Nhb" id="SlZ-le-uC1"/>
|
||||
<outlet property="thumbnailView" destination="2UL-NX-Nd1" id="uMq-9r-M4G"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
/**
|
||||
MXKDeviceView class may be used to display the information of a user's device.
|
||||
The displayed device may be renamed or removed.
|
||||
*/
|
||||
|
||||
@class MXKDeviceView;
|
||||
@protocol MXKDeviceViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that an alert must be presented.
|
||||
|
||||
@param deviceView the device view.
|
||||
@param alert the alert to present.
|
||||
*/
|
||||
- (void)deviceView:(MXKDeviceView*)deviceView presentAlertController:(UIAlertController*)alert;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
Tells the delegate to dismiss the device view.
|
||||
|
||||
@param deviceView the device view.
|
||||
@param isUpdated tell whether the device was updated (renamed, removed...).
|
||||
*/
|
||||
- (void)dismissDeviceView:(MXKDeviceView*)deviceView didUpdate:(BOOL)isUpdated;
|
||||
|
||||
@end
|
||||
|
||||
@interface MXKDeviceView : MXKView <UIGestureRecognizerDelegate>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *bgView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *containerView;
|
||||
@property (weak, nonatomic) IBOutlet UITextView *textView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *renameButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *deleteButton;
|
||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
|
||||
|
||||
/**
|
||||
Initialize a device view to display the information of a user's device.
|
||||
|
||||
@param device a user's device.
|
||||
@param session the matrix session.
|
||||
@return the newly created instance.
|
||||
*/
|
||||
- (instancetype)initWithDevice:(MXDevice*)device andMatrixSession:(MXSession*)session;
|
||||
|
||||
/**
|
||||
The delegate.
|
||||
*/
|
||||
@property (nonatomic, weak) id<MXKDeviceViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
The default text color in the text view. [UIColor blackColor] by default.
|
||||
*/
|
||||
@property (nonatomic) UIColor *defaultTextColor;
|
||||
|
||||
/**
|
||||
Action registered on 'UIControlEventTouchUpInside' event for each UIButton instance.
|
||||
*/
|
||||
- (IBAction)onButtonPressed:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,481 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKDeviceView.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKConstants.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
static NSAttributedString *verticalWhitespace = nil;
|
||||
|
||||
@interface MXKDeviceView ()
|
||||
{
|
||||
/**
|
||||
The displayed device
|
||||
*/
|
||||
MXDevice *mxDevice;
|
||||
|
||||
/**
|
||||
The matrix session.
|
||||
*/
|
||||
MXSession *mxSession;
|
||||
|
||||
/**
|
||||
The current alert
|
||||
*/
|
||||
UIAlertController *currentAlert;
|
||||
|
||||
/**
|
||||
Current request in progress.
|
||||
*/
|
||||
MXHTTPOperation *mxCurrentOperation;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKDeviceView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// Check whether a nib file is available
|
||||
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
|
||||
|
||||
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
|
||||
if (path)
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
|
||||
}
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKDeviceView class]) bundle:[NSBundle mxk_bundleForClass:[MXKDeviceView class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Add tap recognizer to discard the view on bg view tap
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onBgViewTap:)];
|
||||
[tap setNumberOfTouchesRequired:1];
|
||||
[tap setNumberOfTapsRequired:1];
|
||||
[tap setDelegate:self];
|
||||
[self.bgView addGestureRecognizer:tap];
|
||||
|
||||
// Localize string
|
||||
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateNormal];
|
||||
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateHighlighted];
|
||||
|
||||
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateNormal];
|
||||
[_renameButton setTitle:[MatrixKitL10n rename] forState:UIControlStateHighlighted];
|
||||
|
||||
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateNormal];
|
||||
[_deleteButton setTitle:[MatrixKitL10n delete] forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// Scroll to the top the text view content
|
||||
self.textView.contentOffset = CGPointZero;
|
||||
}
|
||||
|
||||
#pragma mark - Override MXKView
|
||||
|
||||
-(void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
_defaultTextColor = [UIColor blackColor];
|
||||
|
||||
// Add shadow on added view
|
||||
_containerView.layer.cornerRadius = 5;
|
||||
_containerView.layer.shadowOffset = CGSizeMake(0, 1);
|
||||
_containerView.layer.shadowOpacity = 0.5f;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)removeFromSuperviewDidUpdate:(BOOL)isUpdated
|
||||
{
|
||||
if (currentAlert)
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
currentAlert = nil;
|
||||
}
|
||||
|
||||
if (mxCurrentOperation)
|
||||
{
|
||||
[mxCurrentOperation cancel];
|
||||
mxCurrentOperation = nil;
|
||||
}
|
||||
|
||||
if (self.delegate && [self.delegate respondsToSelector:@selector(dismissDeviceView:didUpdate:)])
|
||||
{
|
||||
[self.delegate dismissDeviceView:self didUpdate:isUpdated];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithDevice:(MXDevice*)device andMatrixSession:(MXSession*)session
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
if (self)
|
||||
{
|
||||
mxDevice = device;
|
||||
mxSession = session;
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
if (mxDevice)
|
||||
{
|
||||
// Device information
|
||||
NSMutableAttributedString *deviceInformationString = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n deviceDetailsTitle]
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
|
||||
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n deviceDetailsName]
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:device.displayName.length ? device.displayName : @""
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n deviceDetailsIdentifier]
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:device.deviceId
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n deviceDetailsLastSeen]
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
|
||||
NSDate *lastSeenDate = [NSDate dateWithTimeIntervalSince1970:device.lastSeenTs/1000];
|
||||
|
||||
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
|
||||
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]];
|
||||
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
|
||||
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
|
||||
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
||||
|
||||
NSString *lastSeen = [MatrixKitL10n deviceDetailsLastSeenFormat:device.lastSeenIp :[dateFormatter stringFromDate:lastSeenDate]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:lastSeen
|
||||
attributes:@{NSForegroundColorAttributeName : _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKDeviceView verticalWhitespace]];
|
||||
|
||||
self.textView.attributedText = deviceInformationString;
|
||||
}
|
||||
else
|
||||
{
|
||||
_textView.text = nil;
|
||||
}
|
||||
|
||||
// Hide potential activity indicator
|
||||
[_activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mxDevice = nil;
|
||||
mxSession = nil;
|
||||
}
|
||||
|
||||
+ (NSAttributedString *)verticalWhitespace
|
||||
{
|
||||
if (verticalWhitespace == nil)
|
||||
{
|
||||
verticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}];
|
||||
}
|
||||
return verticalWhitespace;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)onBgViewTap:(UITapGestureRecognizer*)sender
|
||||
{
|
||||
[self removeFromSuperviewDidUpdate:NO];
|
||||
}
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
{
|
||||
if (sender == _cancelButton)
|
||||
{
|
||||
[self removeFromSuperviewDidUpdate:NO];
|
||||
}
|
||||
else if (sender == _renameButton)
|
||||
{
|
||||
[self renameDevice];
|
||||
}
|
||||
else if (sender == _deleteButton)
|
||||
{
|
||||
[self deleteDevice];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)renameDevice
|
||||
{
|
||||
if (!self.delegate)
|
||||
{
|
||||
// Ignore
|
||||
MXLogDebug(@"[MXKDeviceView] Rename device failed, delegate is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
// Prompt the user to enter a device name.
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsRenamePromptTitle]
|
||||
message:[MatrixKitL10n deviceDetailsRenamePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
|
||||
textField.secureTextEntry = NO;
|
||||
textField.placeholder = nil;
|
||||
textField.keyboardType = UIKeyboardTypeDefault;
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
textField.text = self->mxDevice.displayName;
|
||||
}
|
||||
}];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
NSString *text = [self->currentAlert textFields].firstObject.text;
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self.activityIndicator startAnimating];
|
||||
|
||||
self->mxCurrentOperation = [self->mxSession.matrixRestClient setDeviceName:text forDeviceId:self->mxDevice.deviceId success:^{
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
[self removeFromSuperviewDidUpdate:YES];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = self->mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
MXLogDebug(@"[MXKDeviceView] Rename device (%@) failed", self->mxDevice.deviceId);
|
||||
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
[self removeFromSuperviewDidUpdate:NO];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self.delegate deviceView:self presentAlertController:currentAlert];
|
||||
}
|
||||
|
||||
- (void)deleteDevice
|
||||
{
|
||||
if (!self.delegate)
|
||||
{
|
||||
// Ignore
|
||||
MXLogDebug(@"[MXKDeviceView] Delete device failed, delegate is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get an authentication session to prepare device deletion
|
||||
[self.activityIndicator startAnimating];
|
||||
|
||||
mxCurrentOperation = [mxSession.matrixRestClient getSessionToDeleteDeviceByDeviceId:mxDevice.deviceId success:^(MXAuthenticationSession *authSession) {
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
// Check whether the password based type is supported
|
||||
BOOL isPasswordBasedTypeSupported = NO;
|
||||
for (MXLoginFlow *loginFlow in authSession.flows)
|
||||
{
|
||||
if ([loginFlow.type isEqualToString:kMXLoginFlowTypePassword] || [loginFlow.stages indexOfObject:kMXLoginFlowTypePassword] != NSNotFound)
|
||||
{
|
||||
isPasswordBasedTypeSupported = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isPasswordBasedTypeSupported && authSession.session)
|
||||
{
|
||||
// Prompt for a password
|
||||
[self->currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
// Prompt the user before deleting the device.
|
||||
self->currentAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n deviceDetailsDeletePromptTitle] message:[MatrixKitL10n deviceDetailsDeletePromptMessage] preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
|
||||
[self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
|
||||
textField.secureTextEntry = YES;
|
||||
textField.placeholder = nil;
|
||||
textField.keyboardType = UIKeyboardTypeDefault;
|
||||
}];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self->currentAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n submit]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
UITextField *textField = [self->currentAlert textFields].firstObject;
|
||||
self->currentAlert = nil;
|
||||
|
||||
NSString *userId = self->mxSession.myUser.userId;
|
||||
NSDictionary *authParams;
|
||||
|
||||
// Sanity check
|
||||
if (userId)
|
||||
{
|
||||
authParams = @{@"session":authSession.session,
|
||||
@"user": userId,
|
||||
@"password": textField.text,
|
||||
@"type": kMXLoginFlowTypePassword};
|
||||
|
||||
}
|
||||
|
||||
self->mxCurrentOperation = [self->mxSession.matrixRestClient deleteDeviceByDeviceId:self->mxDevice.deviceId authParams:authParams success:^{
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
[self removeFromSuperviewDidUpdate:YES];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = self->mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed", self->mxDevice.deviceId);
|
||||
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
[self removeFromSuperviewDidUpdate:NO];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[self.delegate deviceView:self presentAlertController:self->currentAlert];
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, auth session flow type is not supported", self->mxDevice.deviceId);
|
||||
[self.activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
MXLogDebug(@"[MXKDeviceView] Delete device (%@) failed, unable to get auth session", self->mxDevice.deviceId);
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = self->mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<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" id="8VI-1E-fge" customClass="MXKDeviceView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view alpha="0.5" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="D7c-Zw-n8I">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c3W-Ma-n01">
|
||||
<rect key="frame" x="10" y="74" width="580" height="250"/>
|
||||
<subviews>
|
||||
<textView autoresizesSubviews="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
|
||||
<rect key="frame" x="10" y="10" width="560" height="190"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
|
||||
<rect key="frame" x="57.5" y="210" width="30" height="30"/>
|
||||
<state key="normal" title="OK">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o3Z-D3-2S3">
|
||||
<rect key="frame" x="335" y="210" width="56" height="30"/>
|
||||
<state key="normal" title="Rename">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="hbe-VP-vE5"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
|
||||
<rect key="frame" x="485" y="210" width="45" height="30"/>
|
||||
<state key="normal" title="Delete">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="c3W-Ma-n01" secondAttribute="leading" constant="10" id="ECh-zm-rMb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="J2X-t1-Wuo"/>
|
||||
<constraint firstItem="qkC-t0-Qd0" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="QLy-iD-ead"/>
|
||||
<constraint firstAttribute="bottom" secondItem="o3Z-D3-2S3" secondAttribute="bottom" constant="10" id="RLg-NE-JtP"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="4" id="anR-Ea-ml6"/>
|
||||
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="eJw-AZ-Okc"/>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="c3W-Ma-n01" secondAttribute="top" constant="10" id="ej1-zq-aDf"/>
|
||||
<constraint firstAttribute="centerX" secondItem="o3Z-D3-2S3" secondAttribute="centerX" multiplier="4/5" id="h0U-I5-5dA"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="kLj-CN-wSG"/>
|
||||
<constraint firstAttribute="height" priority="750" constant="250" id="rtj-ON-bkY"/>
|
||||
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="4/7" id="s2R-r3-jqz"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="4za-i5-u2D">
|
||||
<rect key="frame" x="290" y="290" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="D7c-Zw-n8I" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" id="9Md-NO-OM3"/>
|
||||
<constraint firstAttribute="trailing" secondItem="c3W-Ma-n01" secondAttribute="trailing" constant="10" id="F6j-Ia-5EQ"/>
|
||||
<constraint firstItem="D7c-Zw-n8I" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" id="Gzh-sa-sB4"/>
|
||||
<constraint firstItem="c3W-Ma-n01" firstAttribute="height" relation="lessThanOrEqual" secondItem="8VI-1E-fge" secondAttribute="height" constant="-20" id="OE8-Ll-IX0"/>
|
||||
<constraint firstItem="4za-i5-u2D" firstAttribute="centerX" secondItem="8VI-1E-fge" secondAttribute="centerX" id="SGT-wg-tcC"/>
|
||||
<constraint firstItem="c3W-Ma-n01" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="74" id="a6K-LL-lzT"/>
|
||||
<constraint firstItem="4za-i5-u2D" firstAttribute="centerY" secondItem="8VI-1E-fge" secondAttribute="centerY" id="gjb-ga-edx"/>
|
||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="c3W-Ma-n01" secondAttribute="bottom" constant="10" id="oD7-T4-DuW"/>
|
||||
<constraint firstAttribute="bottom" secondItem="D7c-Zw-n8I" secondAttribute="bottom" id="qda-fo-h6c"/>
|
||||
<constraint firstItem="c3W-Ma-n01" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="ryV-yq-aqs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="D7c-Zw-n8I" secondAttribute="trailing" id="wCd-aV-jjY"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="activityIndicator" destination="4za-i5-u2D" id="IH9-BZ-e26"/>
|
||||
<outlet property="bgView" destination="D7c-Zw-n8I" id="iQs-LG-SpD"/>
|
||||
<outlet property="cancelButton" destination="Ump-1C-SzI" id="U8M-nT-JiK"/>
|
||||
<outlet property="containerView" destination="c3W-Ma-n01" id="Stk-lk-Wew"/>
|
||||
<outlet property="deleteButton" destination="qkC-t0-Qd0" id="bRu-al-rte"/>
|
||||
<outlet property="renameButton" destination="o3Z-D3-2S3" id="4tr-TC-EcJ"/>
|
||||
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@protocol MXKEncryptionInfoViewDelegate;
|
||||
|
||||
/**
|
||||
MXKEncryptionInfoView class may be used to display the available information on a encrypted event.
|
||||
The event sender device may be verified, unverified, blocked or unblocked from this view.
|
||||
*/
|
||||
@interface MXKEncryptionInfoView : MXKView
|
||||
|
||||
/**
|
||||
The displayed event
|
||||
*/
|
||||
@property (nonatomic, readonly) MXEvent *mxEvent;
|
||||
|
||||
/**
|
||||
The matrix session.
|
||||
*/
|
||||
@property (nonatomic, readonly) MXSession *mxSession;
|
||||
|
||||
/**
|
||||
The event device info
|
||||
*/
|
||||
@property (nonatomic, readonly) MXDeviceInfo *mxDeviceInfo;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITextView *textView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *cancelButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *verifyButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *blockButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *confirmVerifyButton;
|
||||
|
||||
@property (nonatomic, weak) id<MXKEncryptionInfoViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
Initialise an `MXKEncryptionInfoView` instance based on an encrypted event
|
||||
|
||||
@param event the encrypted event.
|
||||
@param session the related matrix session.
|
||||
@return the newly created instance.
|
||||
*/
|
||||
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session;
|
||||
|
||||
/**
|
||||
Initialise an `MXKEncryptionInfoView` instance based only on a device information.
|
||||
|
||||
@param deviceInfo the device information.
|
||||
@param session the related matrix session.
|
||||
@return the newly created instance.
|
||||
*/
|
||||
- (instancetype)initWithDeviceInfo:(MXDeviceInfo*)deviceInfo andMatrixSession:(MXSession*)session;
|
||||
|
||||
/**
|
||||
The default text color in the text view. [UIColor blackColor] by default.
|
||||
*/
|
||||
@property (nonatomic) UIColor *defaultTextColor;
|
||||
|
||||
/**
|
||||
Action registered on 'UIControlEventTouchUpInside' event for each UIButton instance.
|
||||
*/
|
||||
- (IBAction)onButtonPressed:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@protocol MXKEncryptionInfoViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Called when the user changes the verified state of a device.
|
||||
|
||||
@param encryptionInfoView the view.
|
||||
@param deviceInfo the device that has changed.
|
||||
*/
|
||||
- (void)encryptionInfoView:(MXKEncryptionInfoView*)encryptionInfoView didDeviceInfoVerifiedChange:(MXDeviceInfo*)deviceInfo;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
Called when the user close the view without changing value.
|
||||
|
||||
@param encryptionInfoView the view.
|
||||
*/
|
||||
- (void)encryptionInfoViewDidClose:(MXKEncryptionInfoView*)encryptionInfoView;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,492 @@
|
||||
/*
|
||||
Copyright 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKEncryptionInfoView.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKConstants.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
static NSAttributedString *verticalWhitespace = nil;
|
||||
|
||||
@interface MXKEncryptionInfoView ()
|
||||
{
|
||||
/**
|
||||
Current request in progress.
|
||||
*/
|
||||
MXHTTPOperation *mxCurrentOperation;
|
||||
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKEncryptionInfoView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// Check whether a nib file is available
|
||||
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
|
||||
|
||||
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
|
||||
if (path)
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
|
||||
}
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKEncryptionInfoView class]) bundle:[NSBundle mxk_bundleForClass:[MXKEncryptionInfoView class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Localize string
|
||||
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateNormal];
|
||||
[_cancelButton setTitle:[MatrixKitL10n ok] forState:UIControlStateHighlighted];
|
||||
|
||||
[_confirmVerifyButton setTitle:[MatrixKitL10n roomEventEncryptionVerifyOk] forState:UIControlStateNormal];
|
||||
[_confirmVerifyButton setTitle:[MatrixKitL10n roomEventEncryptionVerifyOk] forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// Scroll to the top the text view content
|
||||
self.textView.contentOffset = CGPointZero;
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
if (mxCurrentOperation)
|
||||
{
|
||||
[mxCurrentOperation cancel];
|
||||
mxCurrentOperation = nil;
|
||||
}
|
||||
|
||||
[super removeFromSuperview];
|
||||
}
|
||||
|
||||
#pragma mark - Override MXKView
|
||||
|
||||
-(void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
_defaultTextColor = [UIColor blackColor];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
if (self)
|
||||
{
|
||||
_mxEvent = event;
|
||||
_mxSession = session;
|
||||
_mxDeviceInfo = nil;
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
[self updateTextViewText];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDeviceInfo:(MXDeviceInfo*)deviceInfo andMatrixSession:(MXSession*)session
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
if (self)
|
||||
{
|
||||
_mxEvent = nil;
|
||||
_mxDeviceInfo = deviceInfo;
|
||||
_mxSession = session;
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
[self updateTextViewText];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
_mxEvent = nil;
|
||||
_mxSession = nil;
|
||||
_mxDeviceInfo = nil;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)updateTextViewText
|
||||
{
|
||||
// Prepare the text view content
|
||||
NSMutableAttributedString *textViewAttributedString = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoTitle]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}];
|
||||
|
||||
if (_mxEvent)
|
||||
{
|
||||
NSString *senderId = _mxEvent.sender;
|
||||
|
||||
if (_mxSession && _mxSession.crypto && !_mxDeviceInfo)
|
||||
{
|
||||
_mxDeviceInfo = [_mxSession.crypto eventDeviceInfo:_mxEvent];
|
||||
|
||||
if (!_mxDeviceInfo)
|
||||
{
|
||||
// Trigger a server request to get the device information for the event sender
|
||||
mxCurrentOperation = [_mxSession.crypto downloadKeys:@[senderId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
// Sanity check: check whether some device information has been retrieved.
|
||||
self->_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
if (self.mxDeviceInfo)
|
||||
{
|
||||
[self updateTextViewText];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
self->mxCurrentOperation = nil;
|
||||
|
||||
MXLogDebug(@"[MXKEncryptionInfoView] Crypto failed to download device info for user: %@", self.mxEvent.sender);
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = self.mxSession.myUser.userId;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Event information
|
||||
NSMutableAttributedString *eventInformationString = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEvent]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
NSString *senderKey = _mxEvent.senderKey;
|
||||
NSString *claimedKey = _mxEvent.keysClaimed[@"ed25519"];
|
||||
NSString *algorithm = _mxEvent.wireContent[@"algorithm"];
|
||||
NSString *sessionId = _mxEvent.wireContent[@"session_id"];
|
||||
|
||||
NSString *decryptionError;
|
||||
if (_mxEvent.decryptionError)
|
||||
{
|
||||
decryptionError = [NSString stringWithFormat:@"** %@ **", _mxEvent.decryptionError.localizedDescription];
|
||||
}
|
||||
|
||||
if (!senderKey.length)
|
||||
{
|
||||
senderKey = [MatrixKitL10n roomEventEncryptionInfoEventNone];
|
||||
}
|
||||
if (!claimedKey.length)
|
||||
{
|
||||
claimedKey = [MatrixKitL10n roomEventEncryptionInfoEventNone];
|
||||
}
|
||||
if (!algorithm.length)
|
||||
{
|
||||
algorithm = [MatrixKitL10n roomEventEncryptionInfoEventUnencrypted];
|
||||
}
|
||||
if (!sessionId.length)
|
||||
{
|
||||
sessionId = [MatrixKitL10n roomEventEncryptionInfoEventNone];
|
||||
}
|
||||
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventUserId] attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:senderId
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventIdentityKey]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:senderKey
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventFingerprintKey]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:claimedKey
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventAlgorithm]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:algorithm
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
if (decryptionError.length)
|
||||
{
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventDecryptionError]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:decryptionError
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
}
|
||||
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoEventSessionId]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:sessionId
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[eventInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[textViewAttributedString appendAttributedString:eventInformationString];
|
||||
}
|
||||
|
||||
// Device information
|
||||
NSMutableAttributedString *deviceInformationString = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDevice]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:15]}];
|
||||
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
if (_mxDeviceInfo)
|
||||
{
|
||||
NSString *name = _mxDeviceInfo.displayName;
|
||||
NSString *deviceId = _mxDeviceInfo.deviceId;
|
||||
NSMutableAttributedString *verification;
|
||||
NSString *fingerprint = _mxDeviceInfo.fingerprint;
|
||||
|
||||
// Display here the Verify and Block buttons except if the device is the current one.
|
||||
_verifyButton.hidden = _blockButton.hidden = [_mxDeviceInfo.deviceId isEqualToString:_mxSession.matrixRestClient.credentials.deviceId];
|
||||
|
||||
switch (_mxDeviceInfo.trustLevel.localVerificationStatus)
|
||||
{
|
||||
case MXDeviceUnknown:
|
||||
case MXDeviceUnverified:
|
||||
{
|
||||
verification = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceNotVerified]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}];
|
||||
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal];
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateNormal];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateHighlighted];
|
||||
break;
|
||||
}
|
||||
case MXDeviceVerified:
|
||||
{
|
||||
verification = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceVerified]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}];
|
||||
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateNormal];
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnverify] forState:UIControlStateHighlighted];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateNormal];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoBlock] forState:UIControlStateHighlighted];
|
||||
|
||||
break;
|
||||
}
|
||||
case MXDeviceBlocked:
|
||||
{
|
||||
verification = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceBlocked]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}];
|
||||
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateNormal];
|
||||
[_verifyButton setTitle:[MatrixKitL10n roomEventEncryptionInfoVerify] forState:UIControlStateHighlighted];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnblock] forState:UIControlStateNormal];
|
||||
[_blockButton setTitle:[MatrixKitL10n roomEventEncryptionInfoUnblock] forState:UIControlStateHighlighted];
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceName]
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:(name.length ? name : @"")
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceId] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:deviceId
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceVerification] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:verification];
|
||||
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceFingerprint] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont boldSystemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:fingerprint
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
[deviceInformationString appendAttributedString:[MXKEncryptionInfoView verticalWhitespace]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unknown device
|
||||
[deviceInformationString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionInfoDeviceUnknown] attributes:@{NSForegroundColorAttributeName: _defaultTextColor, NSFontAttributeName: [UIFont italicSystemFontOfSize:14]}]];
|
||||
}
|
||||
|
||||
[textViewAttributedString appendAttributedString:deviceInformationString];
|
||||
|
||||
self.textView.attributedText = textViewAttributedString;
|
||||
}
|
||||
|
||||
+ (NSAttributedString *)verticalWhitespace
|
||||
{
|
||||
if (verticalWhitespace == nil)
|
||||
{
|
||||
verticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:4]}];
|
||||
}
|
||||
return verticalWhitespace;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
{
|
||||
if (sender == _cancelButton)
|
||||
{
|
||||
[self removeFromSuperview];
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(encryptionInfoViewDidClose:)])
|
||||
{
|
||||
[_delegate encryptionInfoViewDidClose:self];
|
||||
}
|
||||
}
|
||||
// Note: Verify and Block buttons are hidden when the deviceInfo is not available
|
||||
else if (sender == _confirmVerifyButton && _mxDeviceInfo)
|
||||
{
|
||||
[_mxSession.crypto setDeviceVerification:MXDeviceVerified forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
|
||||
|
||||
// Refresh data
|
||||
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
if (self->_delegate)
|
||||
{
|
||||
[self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo];
|
||||
}
|
||||
[self removeFromSuperview];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
[self removeFromSuperview];
|
||||
}];
|
||||
}
|
||||
else if (_mxDeviceInfo)
|
||||
{
|
||||
MXDeviceVerification verificationStatus;
|
||||
|
||||
if (sender == _verifyButton)
|
||||
{
|
||||
verificationStatus = ((_mxDeviceInfo.trustLevel.localVerificationStatus == MXDeviceVerified) ? MXDeviceUnverified : MXDeviceVerified);
|
||||
}
|
||||
else if (sender == _blockButton)
|
||||
{
|
||||
verificationStatus = ((_mxDeviceInfo.trustLevel.localVerificationStatus == MXDeviceBlocked) ? MXDeviceUnverified : MXDeviceBlocked);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unexpected case
|
||||
MXLogDebug(@"[MXKEncryptionInfoView] Invalid button pressed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (verificationStatus == MXDeviceVerified)
|
||||
{
|
||||
// Prompt user
|
||||
NSMutableAttributedString *textViewAttributedString = [[NSMutableAttributedString alloc]
|
||||
initWithString:[MatrixKitL10n roomEventEncryptionVerifyTitle] attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont boldSystemFontOfSize:17]}];
|
||||
|
||||
NSString *message = [MatrixKitL10n roomEventEncryptionVerifyMessage:_mxDeviceInfo.displayName :_mxDeviceInfo.deviceId :_mxDeviceInfo.fingerprint];
|
||||
|
||||
[textViewAttributedString appendAttributedString:[[NSMutableAttributedString alloc]
|
||||
initWithString:message
|
||||
attributes:@{NSForegroundColorAttributeName: _defaultTextColor,
|
||||
NSFontAttributeName: [UIFont systemFontOfSize:14]}]];
|
||||
|
||||
self.textView.attributedText = textViewAttributedString;
|
||||
|
||||
[_cancelButton setTitle:[MatrixKitL10n cancel] forState:UIControlStateNormal];
|
||||
[_cancelButton setTitle:[MatrixKitL10n cancel] forState:UIControlStateHighlighted];
|
||||
_verifyButton.hidden = _blockButton.hidden = YES;
|
||||
_confirmVerifyButton.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
[_mxSession.crypto setDeviceVerification:verificationStatus forDevice:_mxDeviceInfo.deviceId ofUser:_mxDeviceInfo.userId success:^{
|
||||
|
||||
// Refresh data
|
||||
_mxDeviceInfo = [self.mxSession.crypto eventDeviceInfo:self.mxEvent];
|
||||
|
||||
if (self->_delegate)
|
||||
{
|
||||
[self->_delegate encryptionInfoView:self didDeviceInfoVerifiedChange:self.mxDeviceInfo];
|
||||
}
|
||||
|
||||
[self removeFromSuperview];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
[self removeFromSuperview];
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<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" id="8VI-1E-fge" customClass="MXKEncryptionInfoView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView autoresizesSubviews="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
|
||||
<rect key="frame" x="10" y="10" width="580" height="540"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
|
||||
<rect key="frame" x="60" y="560" width="30" height="30"/>
|
||||
<state key="normal" title="OK">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
|
||||
<rect key="frame" x="355" y="560" width="40" height="30"/>
|
||||
<state key="normal" title="Verify">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="IdO-jn-iTV">
|
||||
<rect key="frame" x="507" y="560" width="38" height="30"/>
|
||||
<state key="normal" title="Block">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="j21-e2-e3C"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="L4h-aG-8UN">
|
||||
<rect key="frame" x="506" y="560" width="40" height="30"/>
|
||||
<state key="normal" title="Verify">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="nKr-w7-ECp"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="L4h-aG-8UN" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="0tX-be-JRZ"/>
|
||||
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="0.8" id="7iU-9D-miY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="DyR-rX-nt0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="Nfo-nh-gvW"/>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="10" id="Oi3-Zq-HKf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="L4h-aG-8UN" secondAttribute="bottom" constant="10" id="Owt-zM-W2X"/>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="Tqb-eF-AIh"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="4" id="YMC-ob-tqT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="IdO-jn-iTV" secondAttribute="bottom" constant="10" id="YcV-iN-9WU"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="ZbI-rp-TR9"/>
|
||||
<constraint firstAttribute="centerX" secondItem="L4h-aG-8UN" secondAttribute="centerX" multiplier="0.57" id="g1v-Mg-fQ6"/>
|
||||
<constraint firstAttribute="centerX" secondItem="IdO-jn-iTV" secondAttribute="centerX" multiplier="0.57" id="gAm-ph-zPW"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="blockButton" destination="IdO-jn-iTV" id="HI0-ZU-esJ"/>
|
||||
<outlet property="cancelButton" destination="Ump-1C-SzI" id="U8M-nT-JiK"/>
|
||||
<outlet property="confirmVerifyButton" destination="L4h-aG-8UN" id="GX6-dB-JG6"/>
|
||||
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
|
||||
<outlet property="verifyButton" destination="qkC-t0-Qd0" id="6LD-04-WgO"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Copyright 2017 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/UIKit.h>
|
||||
|
||||
@class MXSession, MXKViewController, MXKRoomDataSource;
|
||||
|
||||
/**
|
||||
`MXKEncryptionKeysExportView` is a dialog to export encryption keys from
|
||||
the user's crypto store.
|
||||
*/
|
||||
@interface MXKEncryptionKeysExportView : NSObject
|
||||
|
||||
/**
|
||||
The UIAlertController instance which handles the dialog.
|
||||
*/
|
||||
@property (nonatomic, readonly) UIAlertController *alertController;
|
||||
|
||||
/**
|
||||
The minimum length of the passphrase. 1 by default.
|
||||
*/
|
||||
@property (nonatomic) NSUInteger passphraseMinLength;
|
||||
|
||||
/**
|
||||
Create the `MXKEncryptionKeysExportView` instance.
|
||||
|
||||
@param mxSession the mxSession to export keys from.
|
||||
@return the newly created MXKEncryptionKeysExportView instance.
|
||||
*/
|
||||
- (instancetype)initWithMatrixSession:(MXSession*)mxSession;
|
||||
|
||||
/**
|
||||
Show the dialog in a given view controller.
|
||||
|
||||
@param mxkViewController the mxkViewController where to show the dialog.
|
||||
@param keyFile the path where to export keys to.
|
||||
@param onComplete a block called when the operation is done.
|
||||
*/
|
||||
- (void)showInViewController:(MXKViewController*)mxkViewController toExportKeysToFile:(NSURL*)keyFile onComplete:(void(^)(BOOL success))onComplete;
|
||||
|
||||
|
||||
/**
|
||||
Show the dialog in a given view controller.
|
||||
|
||||
@param viewController the UIViewController where to show the dialog.
|
||||
@param keyFile the path where to export keys to.
|
||||
@param onLoading a block called when to show a spinner.
|
||||
@param onComplete a block called when the operation is done.
|
||||
*/
|
||||
- (void)showInUIViewController:(UIViewController*)viewController
|
||||
toExportKeysToFile:(NSURL*)keyFile
|
||||
onLoading:(void(^)(BOOL onLoading))onLoading
|
||||
onComplete:(void(^)(BOOL success))onComplete;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
Copyright 2017 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 "MXKEncryptionKeysExportView.h"
|
||||
|
||||
#import "MXKViewController.h"
|
||||
#import "MXKRoomDataSource.h"
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@interface MXKEncryptionKeysExportView ()
|
||||
{
|
||||
MXSession *mxSession;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKEncryptionKeysExportView
|
||||
|
||||
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
mxSession = matrixSession;
|
||||
_passphraseMinLength = 1;
|
||||
|
||||
_alertController = [UIAlertController alertControllerWithTitle:[MatrixKitL10n e2eExportRoomKeys] message:[MatrixKitL10n e2eExportPrompt] preferredStyle:UIAlertControllerStyleAlert];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)showInViewController:(MXKViewController *)mxkViewController toExportKeysToFile:(NSURL *)keyFile onComplete:(void (^)(BOOL success))onComplete
|
||||
{
|
||||
[self showInUIViewController:mxkViewController toExportKeysToFile:keyFile onLoading:^(BOOL onLoading) {
|
||||
if (onLoading)
|
||||
{
|
||||
[mxkViewController startActivityIndicator];
|
||||
}
|
||||
else
|
||||
{
|
||||
[mxkViewController stopActivityIndicator];
|
||||
}
|
||||
} onComplete:onComplete];
|
||||
}
|
||||
|
||||
- (void)showInUIViewController:(UIViewController*)viewController
|
||||
toExportKeysToFile:(NSURL*)keyFile
|
||||
onLoading:(void(^)(BOOL onLoading))onLoading
|
||||
onComplete:(void(^)(BOOL success))onComplete
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
// Finalise the dialog
|
||||
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
|
||||
{
|
||||
textField.secureTextEntry = YES;
|
||||
textField.placeholder = [MatrixKitL10n e2ePassphraseCreate];
|
||||
[textField resignFirstResponder];
|
||||
}];
|
||||
|
||||
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
|
||||
{
|
||||
textField.secureTextEntry = YES;
|
||||
textField.placeholder = [MatrixKitL10n e2ePassphraseConfirm];
|
||||
[textField resignFirstResponder];
|
||||
}];
|
||||
|
||||
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onComplete(NO);
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n e2eExport]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
// Retrieve the password and confirmation
|
||||
UITextField *textField = [self.alertController textFields].firstObject;
|
||||
NSString *password = textField.text;
|
||||
|
||||
textField = [self.alertController textFields][1];
|
||||
NSString *confirmation = textField.text;
|
||||
|
||||
// Check they are valid
|
||||
if (password.length < self.passphraseMinLength || ![password isEqualToString:confirmation])
|
||||
{
|
||||
NSString *error;
|
||||
if (!password.length)
|
||||
{
|
||||
error = [MatrixKitL10n e2ePassphraseEmpty];
|
||||
}
|
||||
else if (password.length < self.passphraseMinLength)
|
||||
{
|
||||
error = [MatrixKitL10n e2ePassphraseTooShort:self.passphraseMinLength];
|
||||
}
|
||||
else
|
||||
{
|
||||
error = [MatrixKitL10n e2ePassphraseNotMatch];
|
||||
}
|
||||
|
||||
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onComplete(NO);
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[viewController presentViewController:otherAlert animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start the export process
|
||||
onLoading(YES);
|
||||
|
||||
[self->mxSession.crypto exportRoomKeysWithPassword:password success:^(NSData *keyFileData) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onLoading(NO);
|
||||
|
||||
// Write the result to the passed file
|
||||
[keyFileData writeToURL:keyFile atomically:YES];
|
||||
onComplete(YES);
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onLoading(NO);
|
||||
|
||||
// TODO: i18n the error
|
||||
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onComplete(NO);
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[viewController presentViewController:otherAlert animated:YES completion:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
|
||||
|
||||
// And show it
|
||||
[viewController presentViewController:_alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2017 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/UIKit.h>
|
||||
|
||||
@class MXSession, MXKViewController;
|
||||
|
||||
/**
|
||||
`MXKEncryptionKeysImportView` is a dialog to import encryption keys into
|
||||
the user's crypto store.
|
||||
*/
|
||||
@interface MXKEncryptionKeysImportView : NSObject
|
||||
|
||||
/**
|
||||
The UIAlertController instance which handles the dialog.
|
||||
*/
|
||||
@property (nonatomic, readonly) UIAlertController *alertController;
|
||||
|
||||
/**
|
||||
Create the `MXKEncryptionKeysImportView` instance.
|
||||
|
||||
@param mxSession the mxSession to import keys to.
|
||||
@return the newly created MXKEncryptionKeysImportView instance.
|
||||
*/
|
||||
- (instancetype)initWithMatrixSession:(MXSession*)mxSession;
|
||||
|
||||
/**
|
||||
Show the dialog in a given view controller.
|
||||
|
||||
@param mxkViewController the mxkViewController where to show the dialog.
|
||||
@param fileURL the url of the keys file.
|
||||
@param onComplete a block called when the operation is done (whatever it succeeded or failed).
|
||||
*/
|
||||
- (void)showInViewController:(MXKViewController*)mxkViewController toImportKeys:(NSURL*)fileURL onComplete:(void(^)(void))onComplete;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
Copyright 2017 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 "MXKEncryptionKeysImportView.h"
|
||||
|
||||
#import "MXKViewController.h"
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@interface MXKEncryptionKeysImportView ()
|
||||
{
|
||||
MXSession *mxSession;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKEncryptionKeysImportView
|
||||
|
||||
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
mxSession = matrixSession;
|
||||
|
||||
_alertController = [UIAlertController alertControllerWithTitle:[MatrixKitL10n e2eImportRoomKeys] message:[MatrixKitL10n e2eImportPrompt] preferredStyle:UIAlertControllerStyleAlert];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)showInViewController:(MXKViewController*)mxkViewController toImportKeys:(NSURL*)fileURL onComplete:(void(^)(void))onComplete
|
||||
{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
// Finalise the dialog
|
||||
[_alertController addTextFieldWithConfigurationHandler:^(UITextField *textField)
|
||||
{
|
||||
textField.secureTextEntry = YES;
|
||||
textField.placeholder = [MatrixKitL10n e2ePassphraseEnter];
|
||||
[textField resignFirstResponder];
|
||||
}];
|
||||
|
||||
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onComplete();
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[_alertController addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n e2eImport]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
// Retrieve the password
|
||||
UITextField *textField = [self.alertController textFields].firstObject;
|
||||
NSString *password = textField.text;
|
||||
|
||||
// Start the import process
|
||||
[mxkViewController startActivityIndicator];
|
||||
[self->mxSession.crypto importRoomKeys:[NSData dataWithContentsOfURL:fileURL] withPassword:password success:^(NSUInteger total, NSUInteger imported) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
[mxkViewController stopActivityIndicator];
|
||||
onComplete();
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
[mxkViewController stopActivityIndicator];
|
||||
|
||||
// TODO: i18n the error
|
||||
UIAlertController *otherAlert = [UIAlertController alertControllerWithTitle:[MatrixKitL10n error] message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[otherAlert addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n ok] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
onComplete();
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[mxkViewController presentViewController:otherAlert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
// And show it
|
||||
[mxkViewController presentViewController:_alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2017 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 "MXKTableViewCell.h"
|
||||
|
||||
#import "MXKCellRendering.h"
|
||||
|
||||
#import "MXKGroupCellDataStoring.h"
|
||||
|
||||
/**
|
||||
`MXKGroupTableViewCell` instances display a group.
|
||||
*/
|
||||
@interface MXKGroupTableViewCell : MXKTableViewCell <MXKCellRendering>
|
||||
{
|
||||
@protected
|
||||
/**
|
||||
The current cell data displayed by the table view cell
|
||||
*/
|
||||
id<MXKGroupCellDataStoring> groupCellData;
|
||||
}
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *groupName;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *groupDescription;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *memberCount;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2017 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 "MXKGroupTableViewCell.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@implementation MXKGroupTableViewCell
|
||||
@synthesize delegate;
|
||||
|
||||
#pragma mark - Class methods
|
||||
|
||||
- (void)render:(MXKCellData *)cellData
|
||||
{
|
||||
groupCellData = (id<MXKGroupCellDataStoring>)cellData;
|
||||
if (groupCellData)
|
||||
{
|
||||
// Render the current group values.
|
||||
_groupName.text = groupCellData.groupDisplayname;
|
||||
_groupDescription.text = groupCellData.group.profile.shortDescription;
|
||||
|
||||
if (_groupDescription.text.length)
|
||||
{
|
||||
_groupDescription.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide and fill the label with a fake description to harmonize the height of all the cells.
|
||||
// This is a drawback of the self-sizing cell.
|
||||
_groupDescription.hidden = YES;
|
||||
_groupDescription.text = @"No description";
|
||||
}
|
||||
|
||||
if (_memberCount)
|
||||
{
|
||||
if (groupCellData.group.summary.usersSection.totalUserCountEstimate > 1)
|
||||
{
|
||||
_memberCount.text = [MatrixKitL10n numMembersOther:@(groupCellData.group.summary.usersSection.totalUserCountEstimate).stringValue];
|
||||
}
|
||||
else if (groupCellData.group.summary.usersSection.totalUserCountEstimate == 1)
|
||||
{
|
||||
_memberCount.text = [MatrixKitL10n numMembersOne:@(1).stringValue];
|
||||
}
|
||||
else
|
||||
{
|
||||
_memberCount.text = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_groupName.text = nil;
|
||||
_groupDescription.text = nil;
|
||||
_memberCount.text = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (MXKCellData*)renderedCellData
|
||||
{
|
||||
return groupCellData;
|
||||
}
|
||||
|
||||
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
|
||||
{
|
||||
// The height is fixed
|
||||
//@TODO: change this to handle dynamic font
|
||||
return 70;
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
|
||||
groupCellData = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="L2L-l5-wPx" customClass="MXKGroupTableViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="L2L-l5-wPx" id="aXz-IR-jj5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="69.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="RoomTitle" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Lg1-xQ-AGn">
|
||||
<rect key="frame" x="8" y="8" width="506" height="23"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="19"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="LastEventDescription" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dQt-mN-T6b">
|
||||
<rect key="frame" x="8" y="42" width="584" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="xx users" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="360-Go-RcG">
|
||||
<rect key="frame" x="520" y="8" width="72" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="72" id="uOj-6w-G8q"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="8" id="3qc-BC-FYt"/>
|
||||
<constraint firstItem="360-Go-RcG" firstAttribute="leading" secondItem="Lg1-xQ-AGn" secondAttribute="trailing" constant="6" id="8ff-tS-Ulq"/>
|
||||
<constraint firstItem="dQt-mN-T6b" firstAttribute="leading" secondItem="aXz-IR-jj5" secondAttribute="leading" constant="8" id="UYQ-HG-YP1"/>
|
||||
<constraint firstItem="360-Go-RcG" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="8" id="XyO-tl-6SX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="360-Go-RcG" secondAttribute="trailing" constant="8" id="YqC-WC-Wqe"/>
|
||||
<constraint firstAttribute="bottom" secondItem="dQt-mN-T6b" secondAttribute="bottom" constant="9" id="sLb-Q4-AFW"/>
|
||||
<constraint firstItem="Lg1-xQ-AGn" firstAttribute="top" secondItem="aXz-IR-jj5" secondAttribute="top" constant="8" id="uCT-KE-X09"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dQt-mN-T6b" secondAttribute="trailing" constant="8" id="ygG-1B-fu3"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outlet property="groupDescription" destination="dQt-mN-T6b" id="meF-te-1lf"/>
|
||||
<outlet property="groupName" destination="Lg1-xQ-AGn" id="ABQ-44-bUs"/>
|
||||
<outlet property="memberCount" destination="360-Go-RcG" id="oYv-dg-CoR"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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/Foundation.h>
|
||||
|
||||
#import "MXKCellData.h"
|
||||
|
||||
@protocol MXKCellRenderingDelegate;
|
||||
|
||||
/**
|
||||
`MXKCellRendering` defines a protocol a view must conform to display a cell.
|
||||
|
||||
A cell is a generic term. It can be a UITableViewCell or a UICollectionViewCell or any object
|
||||
expected by the end view controller.
|
||||
*/
|
||||
@protocol MXKCellRendering <NSObject>
|
||||
|
||||
/**
|
||||
* Returns the `UINib` object initialized for the cell.
|
||||
*
|
||||
* @return The initialized `UINib` object or `nil` if there were errors during
|
||||
* initialization or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Configure the cell in order to display the passed data.
|
||||
|
||||
The object implementing the `MXKCellRendering` protocol should be able to cast the past object
|
||||
into its original class.
|
||||
|
||||
@param cellData the data object to render.
|
||||
*/
|
||||
- (void)render:(MXKCellData*)cellData;
|
||||
|
||||
/**
|
||||
Compute the height of the cell to display the passed data.
|
||||
|
||||
@TODO: To support correctly the dynamic fonts, we have to remove this method and
|
||||
its use by enabling self sizing cells at the table view level.
|
||||
When we create a self-sizing table view cell, we need to set the property `estimatedRowHeight` of the table view
|
||||
and use constraints to define the cell’s size.
|
||||
|
||||
@param cellData the data object to render.
|
||||
@param maxWidth the maximum available width.
|
||||
@return the cell height
|
||||
*/
|
||||
+ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth;
|
||||
|
||||
@optional
|
||||
|
||||
/**
|
||||
User's actions delegate.
|
||||
*/
|
||||
@property (nonatomic, weak) id<MXKCellRenderingDelegate> delegate;
|
||||
|
||||
/**
|
||||
This optional getter allow to retrieve the data object currently rendered by the cell.
|
||||
|
||||
@return the current rendered data object.
|
||||
*/
|
||||
- (MXKCellData*)renderedCellData;
|
||||
|
||||
/**
|
||||
Reset the cell.
|
||||
|
||||
The cell is no more displayed. This is time to release resources and removing listeners.
|
||||
In case of UITableViewCell or UIContentViewCell object, the cell must reset in a state
|
||||
that it can be reusable.
|
||||
*/
|
||||
- (void)didEndDisplay;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
`MXKCellRenderingDelegate` defines a protocol used when the user has interactions with
|
||||
the cell view.
|
||||
*/
|
||||
@protocol MXKCellRenderingDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that a user action (button pressed, tap, long press...) has been observed in the cell.
|
||||
|
||||
The action is described by the `actionIdentifier` param.
|
||||
This identifier is specific and depends to the cell view class implementing MXKCellRendering.
|
||||
|
||||
@param cell the cell in which gesture has been observed.
|
||||
@param actionIdentifier an identifier indicating the action type (tap, long press...) and which part of the cell is concerned.
|
||||
@param userInfo a dict containing additional information. It depends on actionIdentifier. May be nil.
|
||||
*/
|
||||
- (void)cell:(id<MXKCellRendering>)cell didRecognizeAction:(NSString*)actionIdentifier userInfo:(NSDictionary *)userInfo;
|
||||
|
||||
/**
|
||||
Asks the delegate if a user action (click on a link) can be done.
|
||||
|
||||
The action is described by the `actionIdentifier` param.
|
||||
This identifier is specific and depends to the cell view class implementing MXKCellRendering.
|
||||
|
||||
@param cell the cell in which gesture has been observed.
|
||||
@param actionIdentifier an identifier indicating the action type (link click) and which part of the cell is concerned.
|
||||
@param userInfo a dict containing additional information. It depends on actionIdentifier. May be nil.
|
||||
@param defaultValue the value to return by default if the action is not handled.
|
||||
@return a boolean value which depends on actionIdentifier.
|
||||
*/
|
||||
- (BOOL)cell:(id<MXKCellRendering>)cell shouldDoAction:(NSString*)actionIdentifier userInfo:(NSDictionary *)userInfo defaultValue:(BOOL)defaultValue;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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/UIKit.h>
|
||||
|
||||
/**
|
||||
'MXKCollectionViewCell' class is used to define custom UICollectionViewCell.
|
||||
Each 'MXKCollectionViewCell-inherited' class has its own 'reuseIdentifier'.
|
||||
*/
|
||||
@interface MXKCollectionViewCell : UICollectionViewCell
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for the cell.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during
|
||||
initialization or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
The default reuseIdentifier of the 'MXKCollectionViewCell-inherited' class.
|
||||
*/
|
||||
+ (NSString*)defaultReuseIdentifier;
|
||||
|
||||
/**
|
||||
Customize the rendering of the collection view cell and its subviews (Do nothing by default).
|
||||
This method is called when the view is initialized or prepared for reuse.
|
||||
|
||||
Override this method to customize the collection view cell at the application level.
|
||||
*/
|
||||
- (void)customizeCollectionViewCellRendering;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 "MXKCollectionViewCell.h"
|
||||
|
||||
@implementation MXKCollectionViewCell
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// Check whether a nib file is available
|
||||
NSBundle *mainBundle = [NSBundle bundleForClass:self.class];
|
||||
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
|
||||
if (path)
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString*)defaultReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
[self customizeCollectionViewCellRendering];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
|
||||
[self customizeCollectionViewCellRendering];
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
// Check whether a xib is defined
|
||||
if ([[self class] nib])
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
self.frame = frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
[self customizeCollectionViewCellRendering];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)customizeCollectionViewCellRendering
|
||||
{
|
||||
// Do nothing by default.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKCollectionViewCell.h"
|
||||
|
||||
#import <AVKit/AVKit.h>
|
||||
|
||||
#import "MXKImageView.h"
|
||||
|
||||
/**
|
||||
'MXKMediaCollectionViewCell' class is used to display picture or video thumbnail.
|
||||
*/
|
||||
@interface MXKMediaCollectionViewCell : MXKCollectionViewCell
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *customView;
|
||||
@property (weak, nonatomic) IBOutlet MXKImageView *mxkImageView;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *centerIcon;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomLeftIcon;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *bottomRightIcon;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *topRightIcon;
|
||||
|
||||
/**
|
||||
A potential player used in the cell.
|
||||
*/
|
||||
@property (nonatomic) AVPlayerViewController *moviePlayer;
|
||||
|
||||
/**
|
||||
A potential observer used to update cell display.
|
||||
*/
|
||||
@property (nonatomic) id notificationObserver;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKMediaCollectionViewCell.h"
|
||||
|
||||
@implementation MXKMediaCollectionViewCell
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
[self.moviePlayer.player pause];
|
||||
self.moviePlayer.player = nil;
|
||||
self.moviePlayer = nil;
|
||||
|
||||
// Restore the cell in reusable state
|
||||
self.mxkImageView.hidden = NO;
|
||||
self.mxkImageView.stretchable = NO;
|
||||
// Cancel potential image download
|
||||
self.mxkImageView.enableInMemoryCache = NO;
|
||||
[self.mxkImageView setImageURI:nil
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
previewImage:nil
|
||||
mediaManager:nil];
|
||||
|
||||
self.customView.hidden = YES;
|
||||
self.centerIcon.hidden = YES;
|
||||
|
||||
// Remove added view in custon view
|
||||
NSArray *subViews = self.customView.subviews;
|
||||
for (UIView *view in subViews)
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
// Remove potential media download observer
|
||||
if (self.notificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.notificationObserver];
|
||||
self.notificationObserver = nil;
|
||||
}
|
||||
|
||||
// Remove all gesture recognizers
|
||||
while (self.gestureRecognizers.count)
|
||||
{
|
||||
[self removeGestureRecognizer:self.gestureRecognizers[0]];
|
||||
}
|
||||
self.tag = -1;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self.moviePlayer.player pause];
|
||||
self.moviePlayer.player = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="eCk-zY-LXq" customClass="MXKMediaCollectionViewCell">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TP2-Sr-1hA" userLabel="CustomView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="T1Q-RS-8o6" customClass="MXKImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="l9u-PV-2Kx" userLabel="centerIcon">
|
||||
<rect key="frame" x="28" y="28" width="25" height="25"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="25" id="fdh-Aa-NDo"/>
|
||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="25" id="pCq-on-PJR"/>
|
||||
<constraint firstAttribute="width" constant="25" id="toX-JA-StF"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="25" id="usE-Mq-K8k"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="YB6-Jv-WMr" userLabel="bottomLeftIcon">
|
||||
<rect key="frame" x="8" y="53" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="20" id="fDo-LF-4CI"/>
|
||||
<constraint firstAttribute="height" constant="20" id="yQL-WA-w9j"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="S1K-Sj-lhV" userLabel="topRightIcon">
|
||||
<rect key="frame" x="52" y="7" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="20" id="YJM-RC-2sA"/>
|
||||
<constraint firstAttribute="height" constant="20" id="ozP-cX-0lT"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Yd-df-HbB" userLabel="bottomRightIcon">
|
||||
<rect key="frame" x="52" y="53" width="20" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="20" id="UEZ-5S-KMf"/>
|
||||
<constraint firstAttribute="width" constant="20" id="bbS-D2-SZi"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="5Yd-df-HbB" secondAttribute="trailing" priority="750" constant="8" id="2bi-ya-uC0"/>
|
||||
<constraint firstAttribute="trailing" secondItem="T1Q-RS-8o6" secondAttribute="trailing" id="2mi-ZG-4dV"/>
|
||||
<constraint firstItem="YB6-Jv-WMr" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" priority="750" constant="8" id="43S-Eh-tnF"/>
|
||||
<constraint firstItem="l9u-PV-2Kx" firstAttribute="centerY" secondItem="eCk-zY-LXq" secondAttribute="centerY" id="7oz-CQ-w1O"/>
|
||||
<constraint firstAttribute="bottom" secondItem="T1Q-RS-8o6" secondAttribute="bottom" id="8rM-p9-Vwc"/>
|
||||
<constraint firstItem="T1Q-RS-8o6" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" id="AET-tr-Wti"/>
|
||||
<constraint firstItem="T1Q-RS-8o6" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" id="Fzk-Dy-HI1"/>
|
||||
<constraint firstAttribute="trailing" secondItem="TP2-Sr-1hA" secondAttribute="trailing" id="NWc-zx-iym"/>
|
||||
<constraint firstAttribute="bottom" secondItem="TP2-Sr-1hA" secondAttribute="bottom" id="Psv-4k-pFA"/>
|
||||
<constraint firstAttribute="bottom" secondItem="YB6-Jv-WMr" secondAttribute="bottom" priority="750" constant="7" id="RTC-59-huH"/>
|
||||
<constraint firstAttribute="bottom" secondItem="5Yd-df-HbB" secondAttribute="bottom" priority="750" constant="7" id="S8E-Zu-cIr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="S1K-Sj-lhV" secondAttribute="trailing" priority="750" constant="8" id="XfO-xa-DlO"/>
|
||||
<constraint firstItem="l9u-PV-2Kx" firstAttribute="centerX" secondItem="eCk-zY-LXq" secondAttribute="centerX" id="d32-f1-U3Q"/>
|
||||
<constraint firstItem="TP2-Sr-1hA" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" id="eBE-6d-RBK"/>
|
||||
<constraint firstItem="TP2-Sr-1hA" firstAttribute="leading" secondItem="eCk-zY-LXq" secondAttribute="leading" id="lR0-ej-w9L"/>
|
||||
<constraint firstItem="S1K-Sj-lhV" firstAttribute="top" secondItem="eCk-zY-LXq" secondAttribute="top" priority="750" constant="7" id="neN-7y-A6j"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="bottomLeftIcon" destination="YB6-Jv-WMr" id="EAI-25-P6H"/>
|
||||
<outlet property="bottomRightIcon" destination="5Yd-df-HbB" id="X1R-n5-kOH"/>
|
||||
<outlet property="centerIcon" destination="l9u-PV-2Kx" id="5E4-tR-DBr"/>
|
||||
<outlet property="customView" destination="TP2-Sr-1hA" id="eOK-uE-mrx"/>
|
||||
<outlet property="mxkImageView" destination="T1Q-RS-8o6" id="kPS-Hx-zbu"/>
|
||||
<outlet property="topRightIcon" destination="S1K-Sj-lhV" id="yc9-aM-Rjc"/>
|
||||
</connections>
|
||||
</collectionViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@interface MXKEventDetailsView : MXKView
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UITextView *textView;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *redactButton;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *closeButton;
|
||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
|
||||
|
||||
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2018 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 "MXKEventDetailsView.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKConstants.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@interface MXKEventDetailsView ()
|
||||
{
|
||||
/**
|
||||
The displayed event
|
||||
*/
|
||||
MXEvent *mxEvent;
|
||||
|
||||
/**
|
||||
The matrix session.
|
||||
*/
|
||||
MXSession *mxSession;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKEventDetailsView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// Check whether a nib file is available
|
||||
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
|
||||
|
||||
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
|
||||
if (path)
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
|
||||
}
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKEventDetailsView class]) bundle:[NSBundle mxk_bundleForClass:[MXKEventDetailsView class]]];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Localize string
|
||||
[_redactButton setTitle:[MatrixKitL10n redact] forState:UIControlStateNormal];
|
||||
[_redactButton setTitle:[MatrixKitL10n redact] forState:UIControlStateHighlighted];
|
||||
[_closeButton setTitle:[MatrixKitL10n close] forState:UIControlStateNormal];
|
||||
[_closeButton setTitle:[MatrixKitL10n close] forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
- (instancetype)initWithEvent:(MXEvent*)event andMatrixSession:(MXSession*)session
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
if (self)
|
||||
{
|
||||
mxEvent = event;
|
||||
mxSession = session;
|
||||
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
// Disable redact button by default
|
||||
_redactButton.enabled = NO;
|
||||
|
||||
if (mxEvent)
|
||||
{
|
||||
NSMutableDictionary *eventDict = [NSMutableDictionary dictionaryWithDictionary:mxEvent.JSONDictionary];
|
||||
|
||||
// Remove event type added by SDK
|
||||
[eventDict removeObjectForKey:@"event_type"];
|
||||
// Remove null values and empty dictionaries
|
||||
for (NSString *key in eventDict.allKeys)
|
||||
{
|
||||
if ([[eventDict objectForKey:key] isEqual:[NSNull null]])
|
||||
{
|
||||
[eventDict removeObjectForKey:key];
|
||||
}
|
||||
else if ([[eventDict objectForKey:key] isKindOfClass:[NSDictionary class]])
|
||||
{
|
||||
NSDictionary *dict = [eventDict objectForKey:key];
|
||||
if (!dict.count)
|
||||
{
|
||||
[eventDict removeObjectForKey:key];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSMutableDictionary *updatedDict = [NSMutableDictionary dictionaryWithDictionary:dict];
|
||||
for (NSString *subKey in dict.allKeys)
|
||||
{
|
||||
if ([[dict objectForKey:subKey] isEqual:[NSNull null]])
|
||||
{
|
||||
[updatedDict removeObjectForKey:subKey];
|
||||
}
|
||||
}
|
||||
[eventDict setObject:updatedDict forKey:key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set text view content
|
||||
NSError *error;
|
||||
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:eventDict
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:&error];
|
||||
_textView.text = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||
|
||||
// Check whether the user can redact this event
|
||||
// Do not allow to redact the event that enabled encryption (m.room.encryption)
|
||||
// because it breaks everything
|
||||
if (!mxEvent.isRedactedEvent && mxEvent.eventType != MXEventTypeRoomEncryption)
|
||||
{
|
||||
// Here the event has not been already redacted, check the user's power level
|
||||
MXRoom *mxRoom = [mxSession roomWithRoomId:mxEvent.roomId];
|
||||
if (mxRoom)
|
||||
{
|
||||
MXWeakify(self);
|
||||
[mxRoom state:^(MXRoomState *roomState) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
MXRoomPowerLevels *powerLevels = [roomState powerLevels];
|
||||
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self->mxSession.myUser.userId];
|
||||
if (powerLevels.redact)
|
||||
{
|
||||
if (userPowerLevel >= powerLevels.redact)
|
||||
{
|
||||
self.redactButton.enabled = YES;
|
||||
}
|
||||
}
|
||||
else if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomRedaction])
|
||||
{
|
||||
self.redactButton.enabled = YES;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_textView.text = nil;
|
||||
}
|
||||
|
||||
// Hide potential activity indicator
|
||||
[_activityIndicator stopAnimating];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mxEvent = nil;
|
||||
mxSession = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
{
|
||||
if (sender == _redactButton)
|
||||
{
|
||||
MXRoom *mxRoom = [mxSession roomWithRoomId:mxEvent.roomId];
|
||||
if (mxRoom)
|
||||
{
|
||||
[_activityIndicator startAnimating];
|
||||
[mxRoom redactEvent:mxEvent.eventId reason:nil success:^{
|
||||
|
||||
[self->_activityIndicator stopAnimating];
|
||||
[self removeFromSuperview];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
MXLogDebug(@"[MXKEventDetailsView] Redact event (%@) failed", self->mxEvent.eventId);
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = mxRoom.mxSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
[self->_activityIndicator stopAnimating];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
}
|
||||
else if (sender == _closeButton)
|
||||
{
|
||||
[self removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,72 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<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" id="8VI-1E-fge" customClass="MXKEventDetailsView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" bounces="NO" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Vk-Jx-L6Y">
|
||||
<rect key="frame" x="10" y="10" width="580" height="540"/>
|
||||
<color key="backgroundColor" red="0.90000000000000002" green="0.90000000000000002" blue="0.90000000000000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="mcb-vF-Tre">
|
||||
<rect key="frame" x="290" y="290" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ump-1C-SzI">
|
||||
<rect key="frame" x="126" y="560" width="49" height="30"/>
|
||||
<state key="normal" title="Redact">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="t42-2S-MsY"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qkC-t0-Qd0">
|
||||
<rect key="frame" x="436" y="560" width="39" height="30"/>
|
||||
<state key="normal" title="Close">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="8VI-1E-fge" eventType="touchUpInside" id="ljK-ul-fY2"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="leading" secondItem="8VI-1E-fge" secondAttribute="leading" constant="10" id="1NW-e9-N6b"/>
|
||||
<constraint firstAttribute="centerY" secondItem="mcb-vF-Tre" secondAttribute="centerY" id="6gd-2U-X0P"/>
|
||||
<constraint firstAttribute="centerX" secondItem="qkC-t0-Qd0" secondAttribute="centerX" multiplier="0.66" id="7iU-9D-miY"/>
|
||||
<constraint firstItem="qkC-t0-Qd0" firstAttribute="top" secondItem="3Vk-Jx-L6Y" secondAttribute="bottom" constant="10" id="Ch9-Be-zPy"/>
|
||||
<constraint firstAttribute="bottom" secondItem="qkC-t0-Qd0" secondAttribute="bottom" constant="10" id="DyR-rX-nt0"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Ump-1C-SzI" secondAttribute="bottom" constant="10" id="Nfo-nh-gvW"/>
|
||||
<constraint firstAttribute="trailing" secondItem="3Vk-Jx-L6Y" secondAttribute="trailing" constant="10" id="Tb0-xj-YuB"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Ump-1C-SzI" secondAttribute="centerX" multiplier="2" id="YMC-ob-tqT"/>
|
||||
<constraint firstAttribute="centerX" secondItem="mcb-vF-Tre" secondAttribute="centerX" id="iQ6-t4-4X3"/>
|
||||
<constraint firstItem="3Vk-Jx-L6Y" firstAttribute="top" secondItem="8VI-1E-fge" secondAttribute="top" constant="10" id="paN-Lu-ZgL"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="activityIndicator" destination="mcb-vF-Tre" id="xpi-vr-YgP"/>
|
||||
<outlet property="closeButton" destination="qkC-t0-Qd0" id="B4g-P8-vHe"/>
|
||||
<outlet property="redactButton" destination="Ump-1C-SzI" id="HUY-kK-FP8"/>
|
||||
<outlet property="textView" destination="3Vk-Jx-L6Y" id="uOw-Bq-neN"/>
|
||||
</connections>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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/UIKit.h>
|
||||
#import <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@class MXKAttachment;
|
||||
|
||||
/**
|
||||
Customize UIView in order to display image defined with remote url. Zooming inside the image (Stretching) is supported.
|
||||
*/
|
||||
@interface MXKImageView : MXKView <UIScrollViewDelegate>
|
||||
|
||||
typedef void (^blockMXKImageView_onClick)(MXKImageView *imageView, NSString* title);
|
||||
|
||||
/**
|
||||
Load an image by its Matrix Content URI.
|
||||
The image is loaded from the media cache (if any). If the image is not available yet,
|
||||
it is downloaded from the Matrix media repository only if a media manager instance is provided.
|
||||
|
||||
The image extension is extracted from the provided mime type (if any). By default 'image/jpeg' is considered.
|
||||
|
||||
@param mxContentURI the Matrix Content URI
|
||||
@param mimeType the media mime type, it is used to define the file extension (may be nil).
|
||||
@param orientation the actual orientation of the encoded image (used UIImageOrientationUp by default).
|
||||
@param previewImage image displayed until the actual image is available.
|
||||
@param mediaManager the media manager instance used to download the image if it is not already in cache.
|
||||
*/
|
||||
- (void)setImageURI:(NSString *)mxContentURI
|
||||
withType:(NSString *)mimeType
|
||||
andImageOrientation:(UIImageOrientation)orientation
|
||||
previewImage:(UIImage*)previewImage
|
||||
mediaManager:(MXMediaManager*)mediaManager;
|
||||
|
||||
/**
|
||||
Load an image by its Matrix Content URI to fit a specific view size.
|
||||
|
||||
CAUTION: this method is available only for the unencrypted content.
|
||||
|
||||
The image is loaded from the media cache (if any). If the image is not available yet,
|
||||
it is downloaded from the Matrix media repository only if a media manager instance is provided.
|
||||
The image extension is extracted from the provided mime type (if any). By default 'image/jpeg' is considered.
|
||||
|
||||
@param mxContentURI the Matrix Content URI
|
||||
@param mimeType the media mime type, it is used to define the file extension (may be nil).
|
||||
@param orientation the actual orientation of the encoded image (used UIImageOrientationUp by default).
|
||||
@param previewImage image displayed until the actual image is available.
|
||||
@param mediaManager the media manager instance used to download the image if it is not already in cache.
|
||||
*/
|
||||
- (void)setImageURI:(NSString *)mxContentURI
|
||||
withType:(NSString *)mimeType
|
||||
andImageOrientation:(UIImageOrientation)orientation
|
||||
toFitViewSize:(CGSize)viewSize
|
||||
withMethod:(MXThumbnailingMethod)thumbnailingMethod
|
||||
previewImage:(UIImage*)previewImage
|
||||
mediaManager:(MXMediaManager*)mediaManager;
|
||||
|
||||
/**
|
||||
* Load an image attachment into the image viewer and display the full res image.
|
||||
* This method must be used to display encrypted attachments
|
||||
* @param attachment The attachment
|
||||
*/
|
||||
- (void)setAttachment:(MXKAttachment *)attachment;
|
||||
|
||||
/**
|
||||
* Load an attachment into the image viewer and display its thumbnail, if it has one.
|
||||
* This method must be used to display encrypted attachments
|
||||
* @param attachment The attachment
|
||||
*/
|
||||
- (void)setAttachmentThumb:(MXKAttachment *)attachment;
|
||||
|
||||
/**
|
||||
Toggle display to fullscreen.
|
||||
|
||||
No change is applied on the status bar here, the caller has to handle it.
|
||||
*/
|
||||
- (void)showFullScreen;
|
||||
|
||||
/**
|
||||
The default background color.
|
||||
Default is [UIColor blackColor].
|
||||
*/
|
||||
@property (nonatomic) UIColor *defaultBackgroundColor;
|
||||
|
||||
// Use this boolean to hide activity indicator during image downloading
|
||||
@property (nonatomic) BOOL hideActivityIndicator;
|
||||
|
||||
@property (strong, nonatomic) UIImage *image;
|
||||
@property (nonatomic, readonly) UIImageView *imageView;
|
||||
|
||||
@property (nonatomic) BOOL stretchable;
|
||||
@property (nonatomic, readonly) BOOL fullScreen;
|
||||
|
||||
// the image is cached in memory.
|
||||
// The medias manager uses a LRU cache.
|
||||
// to avoid loading from the file system.
|
||||
@property (nonatomic) BOOL enableInMemoryCache;
|
||||
|
||||
// mediaManager folder where the image is stored
|
||||
@property (nonatomic) NSString* mediaFolder;
|
||||
|
||||
// Let the user defines some custom buttons over the tabbar
|
||||
- (void)setLeftButtonTitle :leftButtonTitle handler:(blockMXKImageView_onClick)handler;
|
||||
- (void)setRightButtonTitle:rightButtonTitle handler:(blockMXKImageView_onClick)handler;
|
||||
|
||||
- (void)dismissSelection;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,925 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKImageView.h"
|
||||
#import "MXKPieChartView.h"
|
||||
#import "MXKAttachment.h"
|
||||
|
||||
#import "MXKTools.h"
|
||||
|
||||
@interface MXKImageView ()
|
||||
{
|
||||
NSString *mxcURI;
|
||||
NSString *mimeType;
|
||||
UIImageOrientation imageOrientation;
|
||||
|
||||
// additional settings used in case of thumbnail.
|
||||
CGSize thumbnailViewSize;
|
||||
MXThumbnailingMethod thumbnailMethod;
|
||||
|
||||
UIImage *currentImage;
|
||||
|
||||
// the loading view is composed with the spinner and a pie chart
|
||||
// the spinner is display until progress > 0
|
||||
UIView *loadingView;
|
||||
UIActivityIndicatorView *waitingDownloadSpinner;
|
||||
MXKPieChartView *pieChartView;
|
||||
UILabel *progressInfoLabel;
|
||||
|
||||
// validation buttons
|
||||
UIButton *leftButton;
|
||||
UIButton *rightButton;
|
||||
|
||||
NSString *leftButtonTitle;
|
||||
NSString *rightButtonTitle;
|
||||
|
||||
blockMXKImageView_onClick leftHandler;
|
||||
blockMXKImageView_onClick rightHandler;
|
||||
|
||||
UIView* bottomBarView;
|
||||
|
||||
// Subviews
|
||||
UIScrollView *scrollView;
|
||||
|
||||
// Current attachment being displayed in the MXKImageView
|
||||
MXKAttachment *currentAttachment;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKImageView
|
||||
@synthesize stretchable, mediaFolder, imageView;
|
||||
|
||||
#define CUSTOM_IMAGE_VIEW_BUTTON_WIDTH 100
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
if (loadingView)
|
||||
{
|
||||
[loadingView removeFromSuperview];
|
||||
loadingView = nil;
|
||||
}
|
||||
|
||||
if (bottomBarView)
|
||||
{
|
||||
[bottomBarView removeFromSuperview];
|
||||
bottomBarView = nil;
|
||||
}
|
||||
|
||||
pieChartView = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Override MXKView
|
||||
|
||||
-(void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
self.backgroundColor = (_defaultBackgroundColor ? _defaultBackgroundColor : [UIColor blackColor]);
|
||||
|
||||
self.contentMode = UIViewContentModeScaleAspectFit;
|
||||
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)startActivityIndicator
|
||||
{
|
||||
// create the views if they don't exist
|
||||
if (!waitingDownloadSpinner)
|
||||
{
|
||||
waitingDownloadSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
|
||||
|
||||
CGRect frame = waitingDownloadSpinner.frame;
|
||||
frame.size.width += 30;
|
||||
frame.size.height += 30;
|
||||
waitingDownloadSpinner.bounds = frame;
|
||||
[waitingDownloadSpinner.layer setCornerRadius:5];
|
||||
}
|
||||
|
||||
if (!loadingView)
|
||||
{
|
||||
loadingView = [[UIView alloc] init];
|
||||
loadingView.frame = waitingDownloadSpinner.bounds;
|
||||
waitingDownloadSpinner.frame = waitingDownloadSpinner.bounds;
|
||||
[loadingView addSubview:waitingDownloadSpinner];
|
||||
loadingView.backgroundColor = [UIColor clearColor];
|
||||
[self addSubview:loadingView];
|
||||
}
|
||||
|
||||
if (!pieChartView)
|
||||
{
|
||||
pieChartView = [[MXKPieChartView alloc] init];
|
||||
pieChartView.frame = loadingView.bounds;
|
||||
pieChartView.progress = 0;
|
||||
pieChartView.progressColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.25];
|
||||
pieChartView.unprogressColor = [UIColor clearColor];
|
||||
|
||||
[loadingView addSubview:pieChartView];
|
||||
}
|
||||
|
||||
// display the download statistics
|
||||
if (_fullScreen && !progressInfoLabel)
|
||||
{
|
||||
progressInfoLabel = [[UILabel alloc] init];
|
||||
progressInfoLabel.backgroundColor = [UIColor whiteColor];
|
||||
progressInfoLabel.textColor = [UIColor blackColor];
|
||||
progressInfoLabel.font = [UIFont systemFontOfSize:8];
|
||||
progressInfoLabel.alpha = 0.25;
|
||||
progressInfoLabel.text = @"";
|
||||
progressInfoLabel.numberOfLines = 0;
|
||||
[progressInfoLabel sizeToFit];
|
||||
[self addSubview:progressInfoLabel];
|
||||
}
|
||||
|
||||
// initvalue
|
||||
loadingView.hidden = NO;
|
||||
pieChartView.progress = 0;
|
||||
|
||||
// Adjust color
|
||||
if ([self.backgroundColor isEqual:[UIColor blackColor]])
|
||||
{
|
||||
waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
|
||||
// a preview image could be displayed
|
||||
// ensure that the white spinner is visible
|
||||
// it could be drawn on a white area
|
||||
waitingDownloadSpinner.backgroundColor = [UIColor darkGrayColor];
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
waitingDownloadSpinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
|
||||
}
|
||||
|
||||
// ensure that the spinner is drawn at the top
|
||||
[loadingView.superview bringSubviewToFront:loadingView];
|
||||
|
||||
// Adjust position
|
||||
CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
|
||||
loadingView.center = center;
|
||||
|
||||
// Start
|
||||
[waitingDownloadSpinner startAnimating];
|
||||
}
|
||||
|
||||
- (void)stopActivityIndicator
|
||||
{
|
||||
if (waitingDownloadSpinner && waitingDownloadSpinner.isAnimating)
|
||||
{
|
||||
[waitingDownloadSpinner stopAnimating];
|
||||
}
|
||||
|
||||
pieChartView.progress = 0;
|
||||
loadingView.hidden = YES;
|
||||
|
||||
if (progressInfoLabel)
|
||||
{
|
||||
[progressInfoLabel removeFromSuperview];
|
||||
progressInfoLabel = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - setters/getters
|
||||
|
||||
- (void)setDefaultBackgroundColor:(UIColor *)defaultBackgroundColor
|
||||
{
|
||||
_defaultBackgroundColor = defaultBackgroundColor;
|
||||
[self customizeViewRendering];
|
||||
}
|
||||
|
||||
- (void)setImage:(UIImage *)anImage
|
||||
{
|
||||
// remove the observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
currentImage = anImage;
|
||||
imageView.image = anImage;
|
||||
|
||||
[self initScrollZoomFactors];
|
||||
}
|
||||
|
||||
- (UIImage*)image
|
||||
{
|
||||
return currentImage;
|
||||
}
|
||||
|
||||
- (void)showFullScreen
|
||||
{
|
||||
// The full screen display mode is supported only if the shared application instance is available.
|
||||
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
|
||||
if (sharedApplication)
|
||||
{
|
||||
_fullScreen = YES;
|
||||
|
||||
[self initLayout];
|
||||
|
||||
if (self.superview)
|
||||
{
|
||||
[super removeFromSuperview];
|
||||
}
|
||||
|
||||
UIWindow *window = [sharedApplication keyWindow];
|
||||
|
||||
self.frame = window.bounds;
|
||||
[window addSubview:self];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
- (IBAction)onButtonToggle:(id)sender
|
||||
{
|
||||
if (sender == leftButton)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->leftHandler(self, self->leftButtonTitle);
|
||||
});
|
||||
}
|
||||
else if (sender == rightButton)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->rightHandler(self, self->rightButtonTitle);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// add a generic button to the bottom view
|
||||
// return the added UIButton
|
||||
- (UIButton*) addbuttonWithTitle:(NSString*)title
|
||||
{
|
||||
UIButton* button = [[UIButton alloc] init];
|
||||
[button setTitle:title forState:UIControlStateNormal];
|
||||
[button setTitle:title forState:UIControlStateHighlighted];
|
||||
|
||||
if (_fullScreen)
|
||||
{
|
||||
// use the same text color as the tabbar
|
||||
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
|
||||
}
|
||||
// TODO
|
||||
// else {
|
||||
// // use the same text color as the tabbar
|
||||
// [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateNormal];
|
||||
// [button setTitleColor:[AppDelegate theDelegate].masterTabBarController.tabBar.tintColor forState:UIControlStateHighlighted];
|
||||
// }
|
||||
|
||||
// keep the bottomView background color
|
||||
button.backgroundColor = [UIColor clearColor];
|
||||
|
||||
[button addTarget:self action:@selector(onButtonToggle:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[bottomBarView addSubview:button];
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
- (void)initScrollZoomFactors
|
||||
{
|
||||
// check if the image can be zoomed
|
||||
if (self.image && self.stretchable && imageView.frame.size.width && imageView.frame.size.height)
|
||||
{
|
||||
// ensure that the content size is properly initialized
|
||||
scrollView.contentSize = scrollView.frame.size;
|
||||
|
||||
// compute the appliable zoom factor
|
||||
// assume that the user does not expect to zoom more than 100%
|
||||
CGSize imageSize = self.image.size;
|
||||
|
||||
CGFloat scaleX = imageSize.width / imageView.frame.size.width;
|
||||
CGFloat scaleY = imageSize.height / imageView.frame.size.height;
|
||||
|
||||
if (scaleX < scaleY)
|
||||
{
|
||||
scaleX = scaleY;
|
||||
}
|
||||
|
||||
if (scaleX < 1.0)
|
||||
{
|
||||
scaleX = 1.0;
|
||||
}
|
||||
|
||||
scrollView.zoomScale = 1.0;
|
||||
scrollView.minimumZoomScale = 1.0;
|
||||
scrollView.maximumZoomScale = scaleX;
|
||||
|
||||
// update the image frame to ensure that it fits to the scrollview frame
|
||||
imageView.frame = scrollView.bounds;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeFromSuperview
|
||||
{
|
||||
[super removeFromSuperview];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
if (pieChartView)
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)initLayout
|
||||
{
|
||||
// create the subviews if they don't exist
|
||||
if (!scrollView)
|
||||
{
|
||||
scrollView = [[UIScrollView alloc] init];
|
||||
scrollView.delegate = self;
|
||||
scrollView.backgroundColor = [UIColor clearColor];
|
||||
[self addSubview:scrollView];
|
||||
|
||||
imageView = [[UIImageView alloc] init];
|
||||
imageView.backgroundColor = [UIColor clearColor];
|
||||
imageView.userInteractionEnabled = YES;
|
||||
imageView.contentMode = self.contentMode;
|
||||
[scrollView addSubview:imageView];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
// call upper layer
|
||||
[super layoutSubviews];
|
||||
|
||||
[self initLayout];
|
||||
|
||||
// the image has been updated
|
||||
if (imageView.image != self.image)
|
||||
{
|
||||
imageView.image = self.image;
|
||||
}
|
||||
|
||||
CGRect tabBarFrame = CGRectZero;
|
||||
UITabBarController *tabBarController = nil;
|
||||
UIEdgeInsets safeAreaInsets = UIEdgeInsetsZero;
|
||||
|
||||
if (leftButtonTitle || rightButtonTitle)
|
||||
{
|
||||
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
|
||||
if (sharedApplication)
|
||||
{
|
||||
safeAreaInsets = [sharedApplication keyWindow].safeAreaInsets;
|
||||
|
||||
UIViewController *rootViewController = [sharedApplication keyWindow].rootViewController;
|
||||
tabBarController = rootViewController.tabBarController;
|
||||
if (!tabBarController && [rootViewController isKindOfClass:[UITabBarController class]])
|
||||
{
|
||||
tabBarController = (UITabBarController*)rootViewController;
|
||||
}
|
||||
}
|
||||
|
||||
if (tabBarController)
|
||||
{
|
||||
tabBarFrame = tabBarController.tabBar.frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Define a default tabBar frame
|
||||
tabBarFrame = CGRectMake(0, 0, self.frame.size.width, 44 + safeAreaInsets.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
// update the scrollview frame
|
||||
CGRect oneSelfFrame = self.frame;
|
||||
CGRect scrollViewFrame = CGRectIntegral(scrollView.frame);
|
||||
|
||||
if (leftButtonTitle || rightButtonTitle)
|
||||
{
|
||||
oneSelfFrame.size.height -= tabBarFrame.size.height;
|
||||
}
|
||||
|
||||
oneSelfFrame = CGRectIntegral(oneSelfFrame);
|
||||
oneSelfFrame.origin = scrollViewFrame.origin = CGPointZero;
|
||||
|
||||
// use integral rect to avoid rounded value issue (float precision)
|
||||
if (!CGRectEqualToRect(oneSelfFrame, scrollViewFrame))
|
||||
{
|
||||
scrollView.frame = oneSelfFrame;
|
||||
imageView.frame = oneSelfFrame;
|
||||
|
||||
[self initScrollZoomFactors];
|
||||
}
|
||||
|
||||
// check if the dedicated buttons are already added
|
||||
if (leftButtonTitle || rightButtonTitle)
|
||||
{
|
||||
|
||||
if (!bottomBarView)
|
||||
{
|
||||
bottomBarView = [[UIView alloc] init];
|
||||
|
||||
if (leftButtonTitle)
|
||||
{
|
||||
leftButton = [self addbuttonWithTitle:leftButtonTitle];
|
||||
}
|
||||
|
||||
rightButton = [[UIButton alloc] init];
|
||||
|
||||
if (rightButtonTitle)
|
||||
{
|
||||
rightButton = [self addbuttonWithTitle:rightButtonTitle];
|
||||
}
|
||||
|
||||
// in fullscreen, display both buttons above the view (do the same if there is no tab bar)
|
||||
if (_fullScreen || tabBarController == nil)
|
||||
{
|
||||
bottomBarView.backgroundColor = [UIColor blackColor];
|
||||
[self addSubview:bottomBarView];
|
||||
}
|
||||
else
|
||||
{
|
||||
// default tabbar background color
|
||||
CGFloat base = 248.0 / 255.0f;
|
||||
bottomBarView.backgroundColor = [UIColor colorWithRed:base green:base blue:base alpha:1.0];
|
||||
|
||||
// Display them over the tabbar
|
||||
[tabBarController.tabBar addSubview:bottomBarView];
|
||||
}
|
||||
}
|
||||
|
||||
if (_fullScreen)
|
||||
{
|
||||
tabBarFrame.origin.y = self.frame.size.height - tabBarFrame.size.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
tabBarFrame.origin.y = 0;
|
||||
}
|
||||
bottomBarView.frame = tabBarFrame;
|
||||
|
||||
if (leftButton)
|
||||
{
|
||||
leftButton.frame = CGRectMake(safeAreaInsets.left, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
|
||||
}
|
||||
|
||||
if (rightButton)
|
||||
{
|
||||
rightButton.frame = CGRectMake(bottomBarView.frame.size.width - CUSTOM_IMAGE_VIEW_BUTTON_WIDTH - safeAreaInsets.right, 0, CUSTOM_IMAGE_VIEW_BUTTON_WIDTH, bottomBarView.frame.size.height - safeAreaInsets.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
if (!loadingView.hidden)
|
||||
{
|
||||
// ensure that the spinner is drawn at the top
|
||||
[loadingView.superview bringSubviewToFront:loadingView];
|
||||
|
||||
// Adjust positions
|
||||
CGPoint center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
|
||||
loadingView.center = center;
|
||||
|
||||
CGRect progressInfoLabelFrame = progressInfoLabel.frame;
|
||||
progressInfoLabelFrame.origin.x = center.x - (progressInfoLabelFrame.size.width / 2);
|
||||
progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
|
||||
progressInfoLabel.frame = progressInfoLabelFrame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHideActivityIndicator:(BOOL)hideActivityIndicator
|
||||
{
|
||||
_hideActivityIndicator = hideActivityIndicator;
|
||||
if (hideActivityIndicator)
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
}
|
||||
else if (mxcURI)
|
||||
{
|
||||
NSString *downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
|
||||
if ([MXMediaManager existingDownloaderWithIdentifier:downloadId])
|
||||
{
|
||||
// Loading is in progress, start activity indicator
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setImageURI:(NSString *)mxContentURI
|
||||
withType:(NSString *)mimeType
|
||||
andImageOrientation:(UIImageOrientation)orientation
|
||||
previewImage:(UIImage*)previewImage
|
||||
mediaManager:(MXMediaManager*)mediaManager
|
||||
{
|
||||
[self setImageURI:mxContentURI
|
||||
withType:mimeType
|
||||
andImageOrientation:orientation
|
||||
isThumbnail:NO
|
||||
previewImage:previewImage
|
||||
mediaManager:mediaManager];
|
||||
}
|
||||
|
||||
- (void)setImageURI:(NSString *)mxContentURI
|
||||
withType:(NSString *)mimeType
|
||||
andImageOrientation:(UIImageOrientation)orientation
|
||||
toFitViewSize:(CGSize)viewSize
|
||||
withMethod:(MXThumbnailingMethod)thumbnailingMethod
|
||||
previewImage:(UIImage*)previewImage
|
||||
mediaManager:(MXMediaManager*)mediaManager
|
||||
{
|
||||
// Store the thumbnail settings
|
||||
thumbnailViewSize = viewSize;
|
||||
thumbnailMethod = thumbnailingMethod;
|
||||
|
||||
[self setImageURI:mxContentURI
|
||||
withType:mimeType
|
||||
andImageOrientation:orientation
|
||||
isThumbnail:YES
|
||||
previewImage:previewImage
|
||||
mediaManager:mediaManager];
|
||||
}
|
||||
|
||||
- (void)setImageURI:(NSString *)mxContentURI
|
||||
withType:(NSString *)mimeType
|
||||
andImageOrientation:(UIImageOrientation)orientation
|
||||
isThumbnail:(BOOL)isThumbnail
|
||||
previewImage:(UIImage*)previewImage
|
||||
mediaManager:(MXMediaManager*)mediaManager
|
||||
{
|
||||
// Remove any pending observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Reset other data
|
||||
currentAttachment = nil;
|
||||
|
||||
mxcURI = mxContentURI;
|
||||
if (!mxcURI)
|
||||
{
|
||||
// Set preview by default
|
||||
self.image = previewImage;
|
||||
return;
|
||||
}
|
||||
|
||||
// Store image orientation
|
||||
imageOrientation = orientation;
|
||||
|
||||
// Store the mime type used to define the cache path of the image.
|
||||
mimeType = mimeType;
|
||||
if (!mimeType.length)
|
||||
{
|
||||
// Set default mime type if no information is available
|
||||
mimeType = @"image/jpeg";
|
||||
}
|
||||
|
||||
// Retrieve the image from cache if any
|
||||
NSString *cacheFilePath;
|
||||
if (isThumbnail)
|
||||
{
|
||||
cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:mxcURI
|
||||
andType:mimeType
|
||||
inFolder:mediaFolder
|
||||
toFitViewSize:thumbnailViewSize
|
||||
withMethod:thumbnailMethod];
|
||||
}
|
||||
else
|
||||
{
|
||||
cacheFilePath = [MXMediaManager cachePathForMatrixContentURI:mxcURI
|
||||
andType:mimeType
|
||||
inFolder:mediaFolder];
|
||||
}
|
||||
|
||||
UIImage* image = _enableInMemoryCache ? [MXMediaManager loadThroughCacheWithFilePath:cacheFilePath] : [MXMediaManager loadPictureFromFilePath:cacheFilePath];
|
||||
if (image)
|
||||
{
|
||||
if (imageOrientation != UIImageOrientationUp)
|
||||
{
|
||||
self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.image = image;
|
||||
}
|
||||
|
||||
[self stopActivityIndicator];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set preview until the image is loaded
|
||||
self.image = previewImage;
|
||||
|
||||
// Check whether the image download is in progress
|
||||
NSString *downloadId;
|
||||
if (isThumbnail)
|
||||
{
|
||||
downloadId = [MXMediaManager thumbnailDownloadIdForMatrixContentURI:mxcURI
|
||||
inFolder:mediaFolder
|
||||
toFitViewSize:thumbnailViewSize
|
||||
withMethod:thumbnailMethod];
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadId = [MXMediaManager downloadIdForMatrixContentURI:mxcURI inFolder:mediaFolder];
|
||||
}
|
||||
|
||||
MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
|
||||
if (!loader && mediaManager)
|
||||
{
|
||||
// Trigger the download
|
||||
if (isThumbnail)
|
||||
{
|
||||
loader = [mediaManager downloadThumbnailFromMatrixContentURI:mxcURI
|
||||
withType:mimeType
|
||||
inFolder:mediaFolder
|
||||
toFitViewSize:thumbnailViewSize
|
||||
withMethod:thumbnailMethod
|
||||
success:nil
|
||||
failure:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
loader = [mediaManager downloadMediaFromMatrixContentURI:mxcURI
|
||||
withType:mimeType
|
||||
inFolder:mediaFolder];
|
||||
}
|
||||
}
|
||||
|
||||
if (loader)
|
||||
{
|
||||
// update the progress UI with the current info
|
||||
if (!_hideActivityIndicator)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
[self updateProgressUI:loader.statisticsDict];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaLoaderStateDidChange:) name:kMXMediaLoaderStateDidChangeNotification object:loader];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAttachment:(MXKAttachment *)attachment
|
||||
{
|
||||
// Remove any pending observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Set default orientation
|
||||
imageOrientation = UIImageOrientationUp;
|
||||
|
||||
mediaFolder = attachment.eventRoomId;
|
||||
mxcURI = attachment.contentURL;
|
||||
mimeType = attachment.contentInfo[@"mimetype"];
|
||||
if (!mimeType.length)
|
||||
{
|
||||
// Set default mime type if no information is available
|
||||
mimeType = @"image/jpeg";
|
||||
}
|
||||
|
||||
// while we wait for the content to download
|
||||
self.image = [attachment getCachedThumbnail];
|
||||
|
||||
if (!_hideActivityIndicator)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
|
||||
currentAttachment = attachment;
|
||||
|
||||
MXWeakify(self);
|
||||
[attachment getImage:^(MXKAttachment *attachment2, UIImage *img) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (self->currentAttachment != attachment2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.image = img;
|
||||
[self stopActivityIndicator];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
|
||||
} failure:^(MXKAttachment *attachment2, NSError *error) {
|
||||
MXLogDebug(@"Unable to fetch image attachment! %@", error);
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (self->currentAttachment != attachment2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self stopActivityIndicator];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
|
||||
}];
|
||||
|
||||
// Check whether the image download is in progress
|
||||
NSString *downloadId = attachment.downloadId;
|
||||
MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
|
||||
if (loader)
|
||||
{
|
||||
// Observer this loader to display progress
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(checkProgressOnMediaLoaderStateChange:)
|
||||
name:kMXMediaLoaderStateDidChangeNotification
|
||||
object:loader];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setAttachmentThumb:(MXKAttachment *)attachment
|
||||
{
|
||||
// Remove any pending observers
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Store image orientation
|
||||
imageOrientation = attachment.thumbnailOrientation;
|
||||
|
||||
mediaFolder = attachment.eventRoomId;
|
||||
|
||||
// Remove the existing image (if any) by using the potential preview.
|
||||
self.image = attachment.previewImage;
|
||||
|
||||
if (!_hideActivityIndicator)
|
||||
{
|
||||
[self startActivityIndicator];
|
||||
}
|
||||
|
||||
currentAttachment = attachment;
|
||||
|
||||
MXWeakify(self);
|
||||
[attachment getThumbnail:^(MXKAttachment *attachment2, UIImage *img) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
||||
if (self->currentAttachment != attachment2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (img && self->imageOrientation != UIImageOrientationUp)
|
||||
{
|
||||
self.image = [UIImage imageWithCGImage:img.CGImage scale:1.0 orientation:self->imageOrientation];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.image = img;
|
||||
}
|
||||
[self stopActivityIndicator];
|
||||
});
|
||||
|
||||
} failure:^(MXKAttachment *attachment2, NSError *error) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (self->currentAttachment != attachment2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self stopActivityIndicator];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateProgressUI:(NSDictionary*)downloadStatsDict
|
||||
{
|
||||
// Sanity check: updateProgressUI may be called while there is no stats available
|
||||
// This happens when the download failed at the very beginning.
|
||||
if (nil == downloadStatsDict)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSNumber* progressNumber = [downloadStatsDict valueForKey:kMXMediaLoaderProgressValueKey];
|
||||
|
||||
if (progressNumber)
|
||||
{
|
||||
pieChartView.progress = progressNumber.floatValue;
|
||||
waitingDownloadSpinner.hidden = YES;
|
||||
}
|
||||
|
||||
if (progressInfoLabel)
|
||||
{
|
||||
NSNumber* downloadRate = [downloadStatsDict valueForKey:kMXMediaLoaderCurrentDataRateKey];
|
||||
|
||||
NSNumber* completedBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderCompletedBytesCountKey];
|
||||
NSNumber* totalBytesCount = [downloadStatsDict valueForKey:kMXMediaLoaderTotalBytesCountKey];
|
||||
|
||||
NSMutableString* text = [[NSMutableString alloc] init];
|
||||
|
||||
if (completedBytesCount && totalBytesCount)
|
||||
{
|
||||
NSString* progressString = [NSString stringWithFormat:@"%@ / %@", [NSByteCountFormatter stringFromByteCount:completedBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile], [NSByteCountFormatter stringFromByteCount:totalBytesCount.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
|
||||
|
||||
[text appendString:progressString];
|
||||
}
|
||||
|
||||
if (downloadRate)
|
||||
{
|
||||
if (completedBytesCount && totalBytesCount)
|
||||
{
|
||||
CGFloat remainimgTime = ((totalBytesCount.floatValue - completedBytesCount.floatValue)) / downloadRate.floatValue;
|
||||
[text appendFormat:@" (%@)", [MXKTools formatSecondsInterval:remainimgTime]];
|
||||
}
|
||||
|
||||
[text appendFormat:@"\n %@/s", [NSByteCountFormatter stringFromByteCount:downloadRate.longLongValue countStyle:NSByteCountFormatterCountStyleFile]];
|
||||
}
|
||||
|
||||
progressInfoLabel.text = text;
|
||||
|
||||
// on multilines, sizeToFit uses the current width
|
||||
// so reset it
|
||||
progressInfoLabel.frame = CGRectZero;
|
||||
|
||||
[progressInfoLabel sizeToFit];
|
||||
|
||||
//
|
||||
CGRect progressInfoLabelFrame = progressInfoLabel.frame;
|
||||
progressInfoLabelFrame.origin.x = self.center.x - (progressInfoLabelFrame.size.width / 2);
|
||||
progressInfoLabelFrame.origin.y = 10 + loadingView.frame.origin.y + loadingView.frame.size.height;
|
||||
progressInfoLabel.frame = progressInfoLabelFrame;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onMediaLoaderStateDidChange:(NSNotification *)notif
|
||||
{
|
||||
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
|
||||
switch (loader.state) {
|
||||
case MXMediaLoaderStateDownloadInProgress:
|
||||
[self updateProgressUI:loader.statisticsDict];
|
||||
break;
|
||||
case MXMediaLoaderStateDownloadCompleted:
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
// update the image
|
||||
UIImage* image = [MXMediaManager loadPictureFromFilePath:loader.downloadOutputFilePath];
|
||||
if (image)
|
||||
{
|
||||
if (imageOrientation != UIImageOrientationUp)
|
||||
{
|
||||
self.image = [UIImage imageWithCGImage:image.CGImage scale:1.0 orientation:imageOrientation];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.image = image;
|
||||
}
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
|
||||
break;
|
||||
}
|
||||
case MXMediaLoaderStateDownloadFailed:
|
||||
case MXMediaLoaderStateCancelled:
|
||||
[self stopActivityIndicator];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:loader];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkProgressOnMediaLoaderStateChange:(NSNotification *)notif
|
||||
{
|
||||
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
|
||||
switch (loader.state) {
|
||||
case MXMediaLoaderStateDownloadInProgress:
|
||||
[self updateProgressUI:loader.statisticsDict];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - buttons management
|
||||
|
||||
- (void)setLeftButtonTitle: aLeftButtonTitle handler:(blockMXKImageView_onClick)handler
|
||||
{
|
||||
leftButtonTitle = aLeftButtonTitle;
|
||||
leftHandler = handler;
|
||||
}
|
||||
|
||||
- (void)setRightButtonTitle:aRightButtonTitle handler:(blockMXKImageView_onClick)handler
|
||||
{
|
||||
rightButtonTitle = aRightButtonTitle;
|
||||
rightHandler = handler;
|
||||
}
|
||||
|
||||
- (void)dismissSelection
|
||||
{
|
||||
if (bottomBarView)
|
||||
{
|
||||
[bottomBarView removeFromSuperview];
|
||||
bottomBarView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
// require to be able to zoom an image
|
||||
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
|
||||
{
|
||||
return self.stretchable ? imageView : nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
MXKMessageTextView is a UITextView subclass with link detection without text selection.
|
||||
*/
|
||||
@interface MXKMessageTextView : UITextView
|
||||
|
||||
// The last hit test location received by the view.
|
||||
@property (nonatomic, readonly) CGPoint lastHitTestLocation;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#import "MXKMessageTextView.h"
|
||||
#import "UITextView+MatrixKit.h"
|
||||
|
||||
@interface MXKMessageTextView()
|
||||
|
||||
@property (nonatomic, readwrite) CGPoint lastHitTestLocation;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MXKMessageTextView
|
||||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
self.lastHitTestLocation = point;
|
||||
return [super hitTest:point withEvent:event];
|
||||
}
|
||||
|
||||
// Indicate to receive a touch event only if a link is hitted.
|
||||
// Otherwise it means that the touch event will pass through and could be received by a view below.
|
||||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
if (![super pointInside:point withEvent:event])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self isThereALinkNearPoint:point];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright 2017 Aram Sargsyan
|
||||
|
||||
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/UIKit.h>
|
||||
#import "MXKView.h"
|
||||
|
||||
@interface MXKPieChartHUD : MXKView
|
||||
|
||||
+ (MXKPieChartHUD *)showLoadingHudOnView:(UIView *)view WithMessage:(NSString *)message;
|
||||
|
||||
- (void)setProgress:(CGFloat)progress;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
Copyright 2017 Aram Sargsyan
|
||||
|
||||
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 "MXKPieChartHUD.h"
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
#import "MXKPieChartView.h"
|
||||
|
||||
@interface MXKPieChartHUD ()
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIView *hudView;
|
||||
@property (weak, nonatomic) IBOutlet MXKPieChartView *pieChartView;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKPieChartHUD
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
[self configureFromNib];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self configureFromNib];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)configureFromNib
|
||||
{
|
||||
NSBundle *bundle = [NSBundle mxk_bundleForClass:self.class];
|
||||
[bundle loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
|
||||
[self customizeViewRendering];
|
||||
|
||||
self.hudView.frame = self.bounds;
|
||||
|
||||
self.clipsToBounds = YES;
|
||||
self.layer.cornerRadius = 10.0;
|
||||
|
||||
[self addSubview:self.hudView];
|
||||
}
|
||||
|
||||
- (void)customizeViewRendering
|
||||
{
|
||||
[super customizeViewRendering];
|
||||
|
||||
self.pieChartView.backgroundColor = [UIColor clearColor];
|
||||
self.pieChartView.progressColor = [UIColor whiteColor];
|
||||
self.pieChartView.unprogressColor = [UIColor clearColor];
|
||||
self.pieChartView.tintColor = [UIColor whiteColor];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
[self.pieChartView.layer setCornerRadius:self.pieChartView.frame.size.width / 2];
|
||||
}
|
||||
|
||||
#pragma mark - Public
|
||||
|
||||
+ (MXKPieChartHUD *)showLoadingHudOnView:(UIView *)view WithMessage:(NSString *)message
|
||||
{
|
||||
MXKPieChartHUD *hud = [[MXKPieChartHUD alloc] init];
|
||||
[view addSubview:hud];
|
||||
|
||||
hud.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
|
||||
NSLayoutConstraint *centerYConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
|
||||
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:160];
|
||||
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:hud attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:105];
|
||||
[NSLayoutConstraint activateConstraints:@[centerXConstraint, centerYConstraint, widthConstraint, heightConstraint]];
|
||||
|
||||
hud.titleLabel.text = message;
|
||||
|
||||
return hud;
|
||||
}
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
{
|
||||
[UIView animateWithDuration:0.2 animations:^{
|
||||
[self.pieChartView setProgress:progress];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12120" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12088"/>
|
||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MXKPieChartHUD">
|
||||
<connections>
|
||||
<outlet property="hudView" destination="iN0-l3-epB" id="og8-jA-KD7"/>
|
||||
<outlet property="pieChartView" destination="f4A-bz-ZFE" id="Yuf-mb-gdi"/>
|
||||
<outlet property="titleLabel" destination="bZG-ub-27R" id="BBm-ry-r4b"/>
|
||||
</connections>
|
||||
</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="446" height="251"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view alpha="0.59999999999999998" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kHt-vK-fvg" userLabel="Overlay View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="446" height="251"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="oXu-qz-7Vy" userLabel="Content View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="446" height="251"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f4A-bz-ZFE" customClass="MXKPieChartView">
|
||||
<rect key="frame" x="200.5" y="88" width="45" height="45"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="f4A-bz-ZFE" secondAttribute="height" multiplier="1:1" id="M4E-Hv-0LA"/>
|
||||
<constraint firstAttribute="width" constant="45" id="S4I-la-2tP"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label111" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="bZG-ub-27R">
|
||||
<rect key="frame" x="10" y="214.5" width="426" height="26.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="22"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="bZG-ub-27R" secondAttribute="bottom" constant="10" id="H0V-Q4-weJ"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bZG-ub-27R" secondAttribute="trailing" priority="750" constant="10" id="MCI-qE-Pih"/>
|
||||
<constraint firstItem="f4A-bz-ZFE" firstAttribute="centerY" secondItem="oXu-qz-7Vy" secondAttribute="centerY" constant="-15" id="QFo-Ym-MUh"/>
|
||||
<constraint firstItem="bZG-ub-27R" firstAttribute="leading" secondItem="oXu-qz-7Vy" secondAttribute="leading" priority="750" constant="10" id="WNL-5a-Umv"/>
|
||||
<constraint firstItem="bZG-ub-27R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="oXu-qz-7Vy" secondAttribute="leading" constant="10" id="Z4o-fX-dWp"/>
|
||||
<constraint firstItem="f4A-bz-ZFE" firstAttribute="centerX" secondItem="oXu-qz-7Vy" secondAttribute="centerX" id="i5g-L8-LGJ"/>
|
||||
<constraint firstItem="bZG-ub-27R" firstAttribute="top" relation="greaterThanOrEqual" secondItem="f4A-bz-ZFE" secondAttribute="bottom" constant="10" id="tGZ-mn-PzZ"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="bZG-ub-27R" secondAttribute="trailing" constant="10" id="ucQ-m2-ZQj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="kHt-vK-fvg" secondAttribute="bottom" id="5VY-8I-dHe"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kHt-vK-fvg" secondAttribute="trailing" id="FHO-W4-hNj"/>
|
||||
<constraint firstAttribute="trailing" secondItem="oXu-qz-7Vy" secondAttribute="trailing" id="KGT-N4-891"/>
|
||||
<constraint firstAttribute="bottom" secondItem="oXu-qz-7Vy" secondAttribute="bottom" id="KpV-xG-hDT"/>
|
||||
<constraint firstItem="oXu-qz-7Vy" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="Xbp-ik-gW0"/>
|
||||
<constraint firstItem="kHt-vK-fvg" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="ZjB-mP-gcc"/>
|
||||
<constraint firstItem="oXu-qz-7Vy" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="a99-RH-B6U"/>
|
||||
<constraint firstItem="kHt-vK-fvg" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="vUg-49-vYQ"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="-105" y="164.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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/UIKit.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@interface MXKPieChartView : MXKView
|
||||
|
||||
/**
|
||||
The current progress level in [0, 1] range.
|
||||
The pie chart is automatically hidden if progress <= 0.
|
||||
It is shown for other progress values.
|
||||
*/
|
||||
@property (nonatomic) CGFloat progress;
|
||||
|
||||
@property (strong, nonatomic) UIColor* progressColor;
|
||||
@property (strong, nonatomic) UIColor* unprogressColor;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKPieChartView.h"
|
||||
|
||||
@interface MXKPieChartView ()
|
||||
{
|
||||
// graphical items
|
||||
CAShapeLayer* backgroundContainerLayer;
|
||||
CAShapeLayer* powerContainerLayer;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKPieChartView
|
||||
|
||||
- (void)setProgress:(CGFloat)progress
|
||||
{
|
||||
// Consider only positive progress value
|
||||
if (progress <= 0)
|
||||
{
|
||||
_progress = 0;
|
||||
self.hidden = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure that the progress value does not excceed 1.0
|
||||
_progress = MIN(progress, 1.0);
|
||||
self.hidden = NO;
|
||||
}
|
||||
|
||||
// defines the view settings
|
||||
CGFloat radius = self.frame.size.width / 2;
|
||||
|
||||
// draw a rounded view
|
||||
[self.layer setCornerRadius:radius];
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
|
||||
// draw the pie
|
||||
CALayer* layer = [self layer];
|
||||
|
||||
// remove any previous drawn layer
|
||||
if (powerContainerLayer)
|
||||
{
|
||||
[powerContainerLayer removeFromSuperlayer];
|
||||
powerContainerLayer = nil;
|
||||
}
|
||||
|
||||
// define default colors
|
||||
if (!_progressColor)
|
||||
{
|
||||
_progressColor = [UIColor redColor];
|
||||
}
|
||||
|
||||
if (!_unprogressColor)
|
||||
{
|
||||
_unprogressColor = [UIColor lightGrayColor];
|
||||
}
|
||||
|
||||
// the background cell color is hidden the cell is selected.
|
||||
// so put in grey the cell background triggers a weird display (the background grey is hidden but not the red part).
|
||||
// add an other layer fixes the UX.
|
||||
if (!backgroundContainerLayer)
|
||||
{
|
||||
backgroundContainerLayer = [CAShapeLayer layer];
|
||||
[backgroundContainerLayer setZPosition:0];
|
||||
[backgroundContainerLayer setStrokeColor:NULL];
|
||||
backgroundContainerLayer.fillColor = _unprogressColor.CGColor;
|
||||
|
||||
// build the path
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGPathMoveToPoint(path, NULL, radius, radius);
|
||||
|
||||
CGPathAddArc(path, NULL, radius, radius, radius, 0 , 2 * M_PI, 0);
|
||||
CGPathCloseSubpath(path);
|
||||
|
||||
[backgroundContainerLayer setPath:path];
|
||||
CFRelease(path);
|
||||
|
||||
// add the sub layer
|
||||
[layer addSublayer:backgroundContainerLayer];
|
||||
}
|
||||
|
||||
if (_progress)
|
||||
{
|
||||
// create the filled layer
|
||||
powerContainerLayer = [CAShapeLayer layer];
|
||||
[powerContainerLayer setZPosition:0];
|
||||
[powerContainerLayer setStrokeColor:NULL];
|
||||
|
||||
// power level is drawn in red
|
||||
powerContainerLayer.fillColor = _progressColor.CGColor;
|
||||
|
||||
// build the path
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
CGPathMoveToPoint(path, NULL, radius, radius);
|
||||
|
||||
CGPathAddArc(path, NULL, radius, radius, radius, -M_PI / 2, (_progress * 2 * M_PI) - (M_PI / 2), 0);
|
||||
CGPathCloseSubpath(path);
|
||||
|
||||
[powerContainerLayer setPath:path];
|
||||
CFRelease(path);
|
||||
|
||||
// add the sub layer
|
||||
[layer addSublayer:powerContainerLayer];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProgressColor:(UIColor *)progressColor
|
||||
{
|
||||
_progressColor = progressColor;
|
||||
self.progress = _progress;
|
||||
}
|
||||
|
||||
- (void)setUnprogressColor:(UIColor *)unprogressColor
|
||||
{
|
||||
_unprogressColor = unprogressColor;
|
||||
|
||||
if (backgroundContainerLayer)
|
||||
{
|
||||
[backgroundContainerLayer removeFromSuperlayer];
|
||||
backgroundContainerLayer = nil;
|
||||
}
|
||||
self.progress = _progress;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, ReadReceiptsAlignment)
|
||||
{
|
||||
/**
|
||||
The latest receipt is displayed on left
|
||||
*/
|
||||
ReadReceiptAlignmentLeft = 0,
|
||||
|
||||
/**
|
||||
The latest receipt is displayed on right
|
||||
*/
|
||||
ReadReceiptAlignmentRight = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
`MXKReceiptSendersContainer` is a view dedicated to display receipt senders by using their avatars.
|
||||
|
||||
This container handles automatically the number of visible avatars. A label is added when avatars are not all visible (see 'moreLabel' property).
|
||||
*/
|
||||
@interface MXKReceiptSendersContainer : MXKView
|
||||
|
||||
/**
|
||||
The maximum number of avatars displayed in the container. 3 by default.
|
||||
*/
|
||||
@property (nonatomic) NSInteger maxDisplayedAvatars;
|
||||
|
||||
/**
|
||||
The space between avatars. 2.0 points by default.
|
||||
*/
|
||||
@property (nonatomic) CGFloat avatarMargin;
|
||||
|
||||
/**
|
||||
The label added beside avatars when avatars are not all visible.
|
||||
*/
|
||||
@property (nonatomic) UILabel* moreLabel;
|
||||
|
||||
/**
|
||||
The more label text color (If set to nil `moreLabel.textColor` use `UIColor.blackColor` as default color).
|
||||
*/
|
||||
@property (nonatomic) UIColor* moreLabelTextColor;
|
||||
|
||||
/*
|
||||
The read receipt objects for details required in the details view
|
||||
*/
|
||||
@property (nonatomic) NSArray <MXReceiptData *> *readReceipts;
|
||||
|
||||
/*
|
||||
The array of the room members that will be displayed in the container
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray <MXRoomMember *> *roomMembers;
|
||||
|
||||
/*
|
||||
The placeholders of the room members that will be shown if the users don't have avatars
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray <UIImage *> *placeholders;
|
||||
|
||||
/**
|
||||
Initializes an `MXKReceiptSendersContainer` object with a frame and a media manager.
|
||||
|
||||
This is the designated initializer.
|
||||
|
||||
@param frame the container frame. Note that avatar will be displayed in full height in this container.
|
||||
@param mediaManager the media manager used to download the matrix user's avatar.
|
||||
@return The newly-initialized MXKReceiptSendersContainer instance
|
||||
*/
|
||||
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager;
|
||||
|
||||
/**
|
||||
Refresh the container content by using the provided room members.
|
||||
|
||||
@param roomMembers list of room members sorted from the latest receipt to the oldest receipt.
|
||||
@param placeHolders list of placeholders, one by room member. Used when url is nil, or during avatar download.
|
||||
@param alignment (see ReadReceiptsAlignment).
|
||||
*/
|
||||
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2018 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 "MXKReceiptSendersContainer.h"
|
||||
|
||||
#import "MXKImageView.h"
|
||||
|
||||
static UIColor* kMoreLabelDefaultcolor;
|
||||
|
||||
@interface MXKReceiptSendersContainer ()
|
||||
|
||||
@property (nonatomic, readwrite) NSArray <MXRoomMember *> *roomMembers;
|
||||
@property (nonatomic, readwrite) NSArray <UIImage *> *placeholders;
|
||||
@property (nonatomic) MXMediaManager *mediaManager;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation MXKReceiptSendersContainer
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [MXKReceiptSendersContainer class])
|
||||
{
|
||||
kMoreLabelDefaultcolor = [UIColor blackColor];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame andMediaManager:(MXMediaManager*)mediaManager
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self)
|
||||
{
|
||||
_mediaManager = mediaManager;
|
||||
_maxDisplayedAvatars = 3;
|
||||
_avatarMargin = 2.0;
|
||||
_moreLabel = nil;
|
||||
_moreLabelTextColor = kMoreLabelDefaultcolor;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)refreshReceiptSenders:(NSArray<MXRoomMember*>*)roomMembers withPlaceHolders:(NSArray<UIImage*>*)placeHolders andAlignment:(ReadReceiptsAlignment)alignment
|
||||
{
|
||||
// Store the room members and placeholders for showing in the details view controller
|
||||
self.roomMembers = roomMembers;
|
||||
self.placeholders = placeHolders;
|
||||
|
||||
// Remove all previous content
|
||||
for (UIView* view in self.subviews)
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
if (_moreLabel)
|
||||
{
|
||||
[_moreLabel removeFromSuperview];
|
||||
_moreLabel = nil;
|
||||
}
|
||||
|
||||
CGRect globalFrame = self.frame;
|
||||
CGFloat side = globalFrame.size.height;
|
||||
CGFloat defaultMoreLabelWidth = side < 20 ? 20 : side;
|
||||
unsigned long count;
|
||||
unsigned long maxDisplayableItems = (int)((globalFrame.size.width - defaultMoreLabelWidth - _avatarMargin) / (side + _avatarMargin));
|
||||
|
||||
maxDisplayableItems = MIN(maxDisplayableItems, _maxDisplayedAvatars);
|
||||
count = MIN(roomMembers.count, maxDisplayableItems);
|
||||
|
||||
int index;
|
||||
|
||||
CGFloat xOff = 0;
|
||||
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
xOff = globalFrame.size.width - (side + _avatarMargin);
|
||||
}
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
MXRoomMember *roomMember = [roomMembers objectAtIndex:index];
|
||||
UIImage *preview = index < placeHolders.count ? placeHolders[index] : nil;
|
||||
|
||||
MXKImageView *imageView = [[MXKImageView alloc] initWithFrame:CGRectMake(xOff, 0, side, side)];
|
||||
imageView.defaultBackgroundColor = [UIColor clearColor];
|
||||
imageView.autoresizingMask = UIViewAutoresizingNone;
|
||||
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
xOff -= side + _avatarMargin;
|
||||
}
|
||||
else
|
||||
{
|
||||
xOff += side + _avatarMargin;
|
||||
}
|
||||
|
||||
[self addSubview:imageView];
|
||||
imageView.enableInMemoryCache = YES;
|
||||
|
||||
[imageView setImageURI:roomMember.avatarUrl
|
||||
withType:nil
|
||||
andImageOrientation:UIImageOrientationUp
|
||||
toFitViewSize:CGSizeMake(side, side)
|
||||
withMethod:MXThumbnailingMethodCrop
|
||||
previewImage:preview
|
||||
mediaManager:_mediaManager];
|
||||
|
||||
[imageView.layer setCornerRadius:imageView.frame.size.width / 2];
|
||||
imageView.clipsToBounds = YES;
|
||||
}
|
||||
|
||||
// Check whether there are more than expected read receipts
|
||||
if (roomMembers.count > maxDisplayableItems)
|
||||
{
|
||||
// Add a more indicator
|
||||
|
||||
// In case of right alignment, adjust the current position by considering the default label width
|
||||
if (alignment == ReadReceiptAlignmentRight && side < defaultMoreLabelWidth)
|
||||
{
|
||||
xOff -= (defaultMoreLabelWidth - side);
|
||||
}
|
||||
|
||||
_moreLabel = [[UILabel alloc] initWithFrame:CGRectMake(xOff, 0, defaultMoreLabelWidth, side)];
|
||||
_moreLabel.text = [NSString stringWithFormat:(alignment == ReadReceiptAlignmentRight) ? @"%tu+" : @"+%tu", roomMembers.count - maxDisplayableItems];
|
||||
_moreLabel.font = [UIFont systemFontOfSize:11];
|
||||
_moreLabel.adjustsFontSizeToFitWidth = YES;
|
||||
_moreLabel.minimumScaleFactor = 0.6;
|
||||
|
||||
// In case of right alignment, adjust the horizontal position according to the actual label width
|
||||
if (alignment == ReadReceiptAlignmentRight)
|
||||
{
|
||||
[_moreLabel sizeToFit];
|
||||
CGRect frame = _moreLabel.frame;
|
||||
if (frame.size.width < defaultMoreLabelWidth)
|
||||
{
|
||||
frame.origin.x += (defaultMoreLabelWidth - frame.size.width);
|
||||
_moreLabel.frame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
_moreLabel.textColor = self.moreLabelTextColor ?: kMoreLabelDefaultcolor;
|
||||
[self addSubview:_moreLabel];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
NSArray* subviews = self.subviews;
|
||||
for (UIView* view in subviews)
|
||||
{
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
if (_moreLabel)
|
||||
{
|
||||
[_moreLabel removeFromSuperview];
|
||||
_moreLabel = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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/UIKit.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@protocol MXKRoomActivitiesViewDelegate;
|
||||
|
||||
/**
|
||||
Customize UIView to display some extra info above the RoomInputToolBar
|
||||
*/
|
||||
@interface MXKRoomActivitiesView : MXKView
|
||||
|
||||
@property (nonatomic) CGFloat height;
|
||||
|
||||
@property (weak, nonatomic) id<MXKRoomActivitiesViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for a `MXKRoomActivitiesView`.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during initialization
|
||||
or the nib file could not be located.
|
||||
|
||||
@discussion You may override this method to provide a customized nib. If you do,
|
||||
you should also override `roomActivitiesView` to return your
|
||||
view controller loaded from your custom nib.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Creates and returns a new `MXKRoomActivitiesView-inherited` object.
|
||||
|
||||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
@return An initialized `MXKRoomActivitiesView-inherited` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)roomActivitiesView;
|
||||
|
||||
/**
|
||||
Dispose any resources and listener.
|
||||
*/
|
||||
- (void)destroy;
|
||||
|
||||
@end
|
||||
|
||||
@protocol MXKRoomActivitiesViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Called when the activities view height changes.
|
||||
|
||||
@param roomActivitiesView the MXKRoomActivitiesView instance.
|
||||
@param oldHeight its previous height.
|
||||
@param newHeight its new height.
|
||||
*/
|
||||
- (void)didChangeHeight:(MXKRoomActivitiesView*)roomActivitiesView oldHeight:(CGFloat)oldHeight newHeight:(CGFloat)newHeight;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKRoomActivitiesView.h"
|
||||
|
||||
@implementation MXKRoomActivitiesView
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// No 'MXKRoomActivitiesView.xib' has been defined yet
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (instancetype)roomActivitiesView
|
||||
{
|
||||
id instance = nil;
|
||||
|
||||
if ([[self class] nib])
|
||||
{
|
||||
@try {
|
||||
instance = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
instance = [[self alloc] initWithFrame:CGRectZero];
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
_delegate = nil;
|
||||
}
|
||||
|
||||
- (CGFloat)height
|
||||
{
|
||||
return self.frame.size.height;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 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 <MatrixSDK/MatrixSDK.h>
|
||||
|
||||
#import "MXKView.h"
|
||||
|
||||
@class MXKRoomCreationView;
|
||||
@protocol MXKRoomCreationViewDelegate <NSObject>
|
||||
|
||||
/**
|
||||
Tells the delegate that an alert must be presented.
|
||||
|
||||
@param creationView the view.
|
||||
@param alertController the alert to present.
|
||||
*/
|
||||
- (void)roomCreationView:(MXKRoomCreationView*)creationView presentAlertController:(UIAlertController*)alertController;
|
||||
|
||||
/**
|
||||
Tells the delegate to open the room with the provided identifier in a specific matrix session.
|
||||
|
||||
@param creationView the view.
|
||||
@param roomId the room identifier.
|
||||
@param mxSession the matrix session in which the room should be available.
|
||||
*/
|
||||
- (void)roomCreationView:(MXKRoomCreationView*)creationView showRoom:(NSString*)roomId withMatrixSession:(MXSession*)mxSession;
|
||||
@end
|
||||
|
||||
/**
|
||||
MXKRoomCreationView instance is a cell dedicated to room creation.
|
||||
Add this view in your app to offer room creation option.
|
||||
*/
|
||||
@interface MXKRoomCreationView : MXKView <UITextFieldDelegate> {
|
||||
@protected
|
||||
UIView *inputAccessoryView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `UINib` object initialized for the tool bar view.
|
||||
*
|
||||
* @return The initialized `UINib` object or `nil` if there were errors during
|
||||
* initialization or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
Creates and returns a new `MXKRoomCreationView-inherited` object.
|
||||
|
||||
@discussion This is the designated initializer for programmatic instantiation.
|
||||
@return An initialized `MXKRoomCreationView-inherited` object if successful, `nil` otherwise.
|
||||
*/
|
||||
+ (instancetype)roomCreationView;
|
||||
|
||||
/**
|
||||
The delegate.
|
||||
*/
|
||||
@property (nonatomic, weak) id<MXKRoomCreationViewDelegate> delegate;
|
||||
|
||||
/**
|
||||
Hide room name field (NO by default).
|
||||
Set YES this property to disable room name edition and hide the related items.
|
||||
*/
|
||||
@property (nonatomic, getter=isRoomNameFieldHidden) BOOL roomNameFieldHidden;
|
||||
|
||||
/**
|
||||
Hide room alias field (NO by default).
|
||||
Set YES this property to disable room alias edition and hide the related items.
|
||||
*/
|
||||
@property (nonatomic, getter=isRoomAliasFieldHidden) BOOL roomAliasFieldHidden;
|
||||
|
||||
/**
|
||||
Hide room participants field (NO by default).
|
||||
Set YES this property to disable room participants edition and hide the related items.
|
||||
*/
|
||||
@property (nonatomic, getter=isParticipantsFieldHidden) BOOL participantsFieldHidden;
|
||||
|
||||
/**
|
||||
The view height which takes into account potential hidden fields
|
||||
*/
|
||||
@property (nonatomic) CGFloat actualFrameHeight;
|
||||
|
||||
/**
|
||||
*/
|
||||
@property (nonatomic) NSArray* mxSessions;
|
||||
|
||||
/**
|
||||
The custom accessory view associated to all text field of this 'MXKRoomCreationView' instance.
|
||||
This view is actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
|
||||
this accessory view when a text field become the first responder.
|
||||
*/
|
||||
@property (readonly) UIView *inputAccessoryView;
|
||||
|
||||
/**
|
||||
UI items
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomNameLabel;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *roomAliasLabel;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *participantsLabel;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *roomNameTextField;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *roomAliasTextField;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *participantsTextField;
|
||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *roomVisibilityControl;
|
||||
@property (weak, nonatomic) IBOutlet UIButton *createRoomBtn;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomNameFieldTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *roomAliasFieldTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *participantsFieldTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *textFieldLeftConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *createRoomBtnTopConstraint;
|
||||
|
||||
/**
|
||||
Action registered to handle text field editing change (UIControlEventEditingChanged).
|
||||
*/
|
||||
- (IBAction)textFieldEditingChanged:(id)sender;
|
||||
|
||||
/**
|
||||
Force dismiss keyboard.
|
||||
*/
|
||||
- (void)dismissKeyboard;
|
||||
|
||||
/**
|
||||
Dispose any resources and listener.
|
||||
*/
|
||||
- (void)destroy;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 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 "MXKRoomCreationView.h"
|
||||
|
||||
#import "MXKConstants.h"
|
||||
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
#import "MXKSwiftHeader.h"
|
||||
|
||||
@interface MXKRoomCreationView ()
|
||||
{
|
||||
UIAlertController *mxSessionPicker;
|
||||
|
||||
// Array of homeserver suffix (NSString instance)
|
||||
NSMutableArray *homeServerSuffixArray;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MXKRoomCreationView
|
||||
@synthesize inputAccessoryView;
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([MXKRoomCreationView class])
|
||||
bundle:[NSBundle bundleForClass:[MXKRoomCreationView class]]];
|
||||
}
|
||||
|
||||
+ (instancetype)roomCreationView
|
||||
{
|
||||
if ([[self class] nib])
|
||||
{
|
||||
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
return [[self alloc] init];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
// Add observer to keep align text fields
|
||||
[_roomNameLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
|
||||
[_roomAliasLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
|
||||
[_participantsLabel addObserver:self forKeyPath:@"text" options:0 context:nil];
|
||||
[self alignTextFields];
|
||||
|
||||
// Finalize setup
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints: NO];
|
||||
|
||||
// Add an accessory view to the text views in order to retrieve keyboard view.
|
||||
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_roomNameTextField.inputAccessoryView = inputAccessoryView;
|
||||
_roomAliasTextField.inputAccessoryView = inputAccessoryView;
|
||||
_participantsTextField.inputAccessoryView = inputAccessoryView;
|
||||
|
||||
// Localize strings
|
||||
_roomNameLabel.text = [MatrixKitL10n roomCreationNameTitle];
|
||||
_roomNameTextField.placeholder = [MatrixKitL10n roomCreationNamePlaceholder];
|
||||
_roomAliasLabel.text = [MatrixKitL10n roomCreationAliasTitle];
|
||||
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholder];
|
||||
_participantsLabel.text = [MatrixKitL10n roomCreationParticipantsTitle];
|
||||
_participantsTextField.placeholder = [MatrixKitL10n roomCreationParticipantsPlaceholder];
|
||||
|
||||
[_roomVisibilityControl setTitle:[MatrixKitL10n public] forSegmentAtIndex:0];
|
||||
[_roomVisibilityControl setTitle:[MatrixKitL10n private] forSegmentAtIndex:1];
|
||||
|
||||
[_createRoomBtn setTitle:[MatrixKitL10n createRoom] forState:UIControlStateNormal];
|
||||
[_createRoomBtn setTitle:[MatrixKitL10n createRoom] forState:UIControlStateHighlighted];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self destroy];
|
||||
|
||||
inputAccessoryView = nil;
|
||||
}
|
||||
|
||||
- (void)setRoomNameFieldHidden:(BOOL)roomNameFieldHidden
|
||||
{
|
||||
_roomNameFieldHidden = _roomNameTextField.hidden = _roomNameLabel.hidden = roomNameFieldHidden;
|
||||
|
||||
if (roomNameFieldHidden)
|
||||
{
|
||||
_roomAliasFieldTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
|
||||
_participantsFieldTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
|
||||
_createRoomBtnTopConstraint.constant -= _roomNameTextField.frame.size.height + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
_roomAliasFieldTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
|
||||
_participantsFieldTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
|
||||
_createRoomBtnTopConstraint.constant += _roomNameTextField.frame.size.height + 8;
|
||||
}
|
||||
|
||||
[self alignTextFields];
|
||||
}
|
||||
|
||||
- (void)setRoomAliasFieldHidden:(BOOL)roomAliasFieldHidden
|
||||
{
|
||||
_roomAliasFieldHidden = _roomAliasTextField.hidden = _roomAliasLabel.hidden = roomAliasFieldHidden;
|
||||
|
||||
if (roomAliasFieldHidden)
|
||||
{
|
||||
_participantsFieldTopConstraint.constant -= _roomAliasTextField.frame.size.height + 8;
|
||||
_createRoomBtnTopConstraint.constant -= _roomAliasTextField.frame.size.height + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
_participantsFieldTopConstraint.constant += _roomAliasTextField.frame.size.height + 8;
|
||||
_createRoomBtnTopConstraint.constant += _roomAliasTextField.frame.size.height + 8;
|
||||
}
|
||||
|
||||
[self alignTextFields];
|
||||
}
|
||||
|
||||
- (void)setParticipantsFieldHidden:(BOOL)participantsFieldHidden
|
||||
{
|
||||
_participantsFieldHidden = _participantsTextField.hidden = _participantsLabel.hidden = participantsFieldHidden;
|
||||
|
||||
if (participantsFieldHidden)
|
||||
{
|
||||
_createRoomBtnTopConstraint.constant -= _participantsTextField.frame.size.height + 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
_createRoomBtnTopConstraint.constant += _participantsTextField.frame.size.height + 8;
|
||||
}
|
||||
|
||||
[self alignTextFields];
|
||||
}
|
||||
|
||||
- (CGFloat)actualFrameHeight
|
||||
{
|
||||
return (_createRoomBtnTopConstraint.constant + _createRoomBtn.frame.size.height + 8);
|
||||
}
|
||||
|
||||
- (void)setMxSessions:(NSArray *)mxSessions
|
||||
{
|
||||
_mxSessions = mxSessions;
|
||||
|
||||
if (mxSessions.count)
|
||||
{
|
||||
homeServerSuffixArray = [NSMutableArray array];
|
||||
|
||||
for (MXSession *mxSession in mxSessions)
|
||||
{
|
||||
NSString *homeserverSuffix = mxSession.matrixRestClient.homeserverSuffix;
|
||||
if (homeserverSuffix && [homeServerSuffixArray indexOfObject:homeserverSuffix] == NSNotFound)
|
||||
{
|
||||
[homeServerSuffixArray addObject:homeserverSuffix];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
homeServerSuffixArray = nil;
|
||||
}
|
||||
|
||||
// Update alias placeholder in room creation section
|
||||
if (homeServerSuffixArray.count == 1)
|
||||
{
|
||||
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholderWithHomeserver:homeServerSuffixArray.firstObject];
|
||||
}
|
||||
else
|
||||
{
|
||||
_roomAliasTextField.placeholder = [MatrixKitL10n roomCreationAliasPlaceholder];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dismissKeyboard
|
||||
{
|
||||
// Hide the keyboard
|
||||
[_roomNameTextField resignFirstResponder];
|
||||
[_roomAliasTextField resignFirstResponder];
|
||||
[_participantsTextField resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void)destroy
|
||||
{
|
||||
self.mxSessions = nil;
|
||||
|
||||
// Remove observers
|
||||
[_roomNameLabel removeObserver:self forKeyPath:@"text"];
|
||||
[_roomAliasLabel removeObserver:self forKeyPath:@"text"];
|
||||
[_participantsLabel removeObserver:self forKeyPath:@"text"];
|
||||
}
|
||||
|
||||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
||||
{
|
||||
// UIView will be "transparent" for touch events if we return NO
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Internal methods
|
||||
|
||||
- (void)alignTextFields
|
||||
{
|
||||
CGFloat maxLabelLenght = 0;
|
||||
|
||||
if (!_roomNameLabel.hidden)
|
||||
{
|
||||
maxLabelLenght = _roomNameLabel.frame.size.width;
|
||||
}
|
||||
if (!_roomAliasLabel.hidden && maxLabelLenght < _roomAliasLabel.frame.size.width)
|
||||
{
|
||||
maxLabelLenght = _roomAliasLabel.frame.size.width;
|
||||
}
|
||||
if (!_participantsLabel.hidden && maxLabelLenght < _participantsLabel.frame.size.width)
|
||||
{
|
||||
maxLabelLenght = _participantsLabel.frame.size.width;
|
||||
}
|
||||
|
||||
// Update textField left constraint by adding marging
|
||||
_textFieldLeftConstraint.constant = maxLabelLenght + (2 * 8);
|
||||
|
||||
[self layoutIfNeeded];
|
||||
}
|
||||
|
||||
- (NSString*)alias
|
||||
{
|
||||
// Extract alias name from alias text field
|
||||
NSString *alias = _roomAliasTextField.text;
|
||||
if (alias.length)
|
||||
{
|
||||
// Remove '#' character
|
||||
alias = [alias substringFromIndex:1];
|
||||
|
||||
NSString *actualAlias = nil;
|
||||
for (NSString *homeServerSuffix in homeServerSuffixArray)
|
||||
{
|
||||
// Remove homeserver suffix
|
||||
NSRange range = [alias rangeOfString:homeServerSuffix];
|
||||
if (range.location != NSNotFound)
|
||||
{
|
||||
actualAlias = [alias stringByReplacingCharactersInRange:range withString:@""];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualAlias)
|
||||
{
|
||||
alias = actualAlias;
|
||||
}
|
||||
else
|
||||
{
|
||||
MXLogDebug(@"[MXKRoomCreationView] Wrong room alias has been set (%@)", _roomAliasTextField.text);
|
||||
alias = nil;
|
||||
}
|
||||
}
|
||||
|
||||
if (! alias.length)
|
||||
{
|
||||
alias = nil;
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
- (NSArray*)participantsList
|
||||
{
|
||||
NSMutableArray *participants = [NSMutableArray array];
|
||||
|
||||
if (_participantsTextField.text.length)
|
||||
{
|
||||
NSArray *components = [_participantsTextField.text componentsSeparatedByString:@";"];
|
||||
|
||||
for (NSString *component in components)
|
||||
{
|
||||
// Remove white space from both ends
|
||||
NSString *user = [component stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||
if (user.length > 1 && [user hasPrefix:@"@"])
|
||||
{
|
||||
[participants addObject:user];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (participants.count == 0)
|
||||
{
|
||||
participants = nil;
|
||||
}
|
||||
|
||||
return participants;
|
||||
}
|
||||
|
||||
- (void)selectMatrixSession:(void (^)(MXSession *selectedSession))onSelection
|
||||
{
|
||||
if (_mxSessions.count == 1)
|
||||
{
|
||||
if (onSelection)
|
||||
{
|
||||
onSelection(_mxSessions.firstObject);
|
||||
}
|
||||
}
|
||||
else if (_mxSessions.count > 1)
|
||||
{
|
||||
if (mxSessionPicker)
|
||||
{
|
||||
[mxSessionPicker dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
|
||||
mxSessionPicker = [UIAlertController alertControllerWithTitle:[MatrixKitL10n selectAccount] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
for(MXSession *mxSession in _mxSessions)
|
||||
{
|
||||
[mxSessionPicker addAction:[UIAlertAction actionWithTitle:mxSession.myUser.userId
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->mxSessionPicker = nil;
|
||||
|
||||
if (onSelection)
|
||||
{
|
||||
onSelection(mxSession);
|
||||
}
|
||||
}
|
||||
|
||||
}]];
|
||||
}
|
||||
|
||||
[mxSessionPicker addAction:[UIAlertAction actionWithTitle:[MatrixKitL10n cancel]
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->mxSessionPicker = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[mxSessionPicker popoverPresentationController].sourceView = self;
|
||||
[mxSessionPicker popoverPresentationController].sourceRect = self.bounds;
|
||||
|
||||
if (self.delegate)
|
||||
{
|
||||
[self.delegate roomCreationView:self presentAlertController:mxSessionPicker];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate
|
||||
|
||||
- (IBAction)textFieldEditingChanged:(id)sender
|
||||
{
|
||||
// Update Create Room button
|
||||
NSString *roomName = _roomNameTextField.text;
|
||||
NSString *roomAlias = _roomAliasTextField.text;
|
||||
NSString *participants = _participantsTextField.text;
|
||||
|
||||
// Room alias is required to create public room
|
||||
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 0) ? roomAlias.length : (roomName.length || roomAlias.length || participants.length));
|
||||
}
|
||||
|
||||
- (void)textFieldDidBeginEditing:(UITextField *)textField
|
||||
{
|
||||
if (textField == _participantsTextField)
|
||||
{
|
||||
if (textField.text.length == 0)
|
||||
{
|
||||
textField.text = @"@";
|
||||
}
|
||||
}
|
||||
else if (textField == _roomAliasTextField)
|
||||
{
|
||||
if (textField.text.length == 0)
|
||||
{
|
||||
textField.text = @"#";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)textFieldDidEndEditing:(UITextField *)textField
|
||||
{
|
||||
if (textField == _roomAliasTextField)
|
||||
{
|
||||
if (homeServerSuffixArray.count == 1)
|
||||
{
|
||||
// Check whether homeserver suffix should be added
|
||||
NSRange range = [textField.text rangeOfString:@":"];
|
||||
if (range.location == NSNotFound)
|
||||
{
|
||||
textField.text = [textField.text stringByAppendingString:homeServerSuffixArray.firstObject];
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether the alias is valid
|
||||
if (!self.alias)
|
||||
{
|
||||
// reset text field
|
||||
textField.text = nil;
|
||||
|
||||
// Update Create button status
|
||||
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 1) && (_roomNameTextField.text.length || _participantsTextField.text.length));
|
||||
}
|
||||
}
|
||||
else if (textField == _participantsTextField)
|
||||
{
|
||||
NSArray *participants = self.participantsList;
|
||||
textField.text = [participants componentsJoinedByString:@"; "];
|
||||
|
||||
// Update Create button status
|
||||
_createRoomBtn.enabled = ((_roomVisibilityControl.selectedSegmentIndex == 0) ? _roomAliasTextField.text.length : (_roomNameTextField.text.length || _roomAliasTextField.text.length || _participantsTextField.text.length));
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
||||
{
|
||||
// Auto complete participant IDs
|
||||
if (textField == _participantsTextField)
|
||||
{
|
||||
// Add @ if none
|
||||
if (!textField.text.length || textField.text.length == range.length)
|
||||
{
|
||||
if ([string hasPrefix:@"@"] == NO)
|
||||
{
|
||||
textField.text = [NSString stringWithFormat:@"@%@",string];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (range.location == textField.text.length)
|
||||
{
|
||||
if ([string isEqualToString:@";"])
|
||||
{
|
||||
// Add '@' character
|
||||
textField.text = [textField.text stringByAppendingString:@"; @"];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (textField == _roomAliasTextField)
|
||||
{
|
||||
// Add # if none
|
||||
if (!textField.text.length || textField.text.length == range.length)
|
||||
{
|
||||
if ([string hasPrefix:@"#"] == NO)
|
||||
{
|
||||
if ([string isEqualToString:@":"] && homeServerSuffixArray.count == 1)
|
||||
{
|
||||
textField.text = [NSString stringWithFormat:@"#%@",homeServerSuffixArray.firstObject];
|
||||
}
|
||||
else
|
||||
{
|
||||
textField.text = [NSString stringWithFormat:@"#%@",string];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
else if (homeServerSuffixArray.count == 1)
|
||||
{
|
||||
// Add homeserver automatically when user adds ':' at the end
|
||||
if (range.location == textField.text.length && [string isEqualToString:@":"])
|
||||
{
|
||||
textField.text = [textField.text stringByAppendingString:homeServerSuffixArray.firstObject];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*) textField
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[textField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
|
||||
- (IBAction)onButtonPressed:(id)sender
|
||||
{
|
||||
[self dismissKeyboard];
|
||||
|
||||
// Handle multi-sessions here
|
||||
[self selectMatrixSession:^(MXSession *selectedSession)
|
||||
{
|
||||
if (sender == self->_createRoomBtn)
|
||||
{
|
||||
// Disable button to prevent multiple request
|
||||
self->_createRoomBtn.enabled = NO;
|
||||
|
||||
MXRoomCreationParameters *roomCreationParameters = [MXRoomCreationParameters new];
|
||||
|
||||
roomCreationParameters.name = self->_roomNameTextField.text;
|
||||
if (!roomCreationParameters.name.length)
|
||||
{
|
||||
roomCreationParameters.name = nil;
|
||||
}
|
||||
|
||||
// Check whether some users must be invited
|
||||
roomCreationParameters.inviteArray = self.participantsList;
|
||||
|
||||
// Prepare room settings
|
||||
|
||||
if (self->_roomVisibilityControl.selectedSegmentIndex == 0)
|
||||
{
|
||||
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPublic;
|
||||
}
|
||||
else
|
||||
{
|
||||
roomCreationParameters.visibility = kMXRoomDirectoryVisibilityPrivate;
|
||||
roomCreationParameters.isDirect = (roomCreationParameters.inviteArray.count == 1);
|
||||
}
|
||||
|
||||
// Ensure direct chat are created with equal ops on both sides (the trusted_private_chat preset)
|
||||
roomCreationParameters.preset = (roomCreationParameters.isDirect ? kMXRoomPresetTrustedPrivateChat : nil);
|
||||
|
||||
// Create new room
|
||||
[selectedSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
|
||||
|
||||
// Reset text fields
|
||||
self->_roomNameTextField.text = nil;
|
||||
self->_roomAliasTextField.text = nil;
|
||||
self->_participantsTextField.text = nil;
|
||||
|
||||
if (self.delegate)
|
||||
{
|
||||
// Open created room
|
||||
[self.delegate roomCreationView:self showRoom:room.roomId withMatrixSession:selectedSession];
|
||||
}
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
self->_createRoomBtn.enabled = YES;
|
||||
|
||||
MXLogDebug(@"[MXKRoomCreationView] Create room (%@ %@ (%@)) failed", self->_roomNameTextField.text, self.alias, (self->_roomVisibilityControl.selectedSegmentIndex == 0) ? @"Public":@"Private");
|
||||
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = selectedSession.myUser.userId;
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification object:error userInfo:myUserId ? @{kMXKErrorUserIdKey: myUserId} : nil];
|
||||
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - KVO
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
// Check whether one label has been updated
|
||||
if ([@"text" isEqualToString:keyPath] && (object == _roomNameLabel || object == _roomAliasLabel || object == _participantsLabel))
|
||||
{
|
||||
// Update left constraint of the text fields
|
||||
[object sizeToFit];
|
||||
[self alignTextFields];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<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" id="pWR-M4-k2c" customClass="MXKRoomCreationView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. lunchGroup)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="IVl-ZW-gpm">
|
||||
<rect key="frame" x="109" y="8" width="483" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="s8S-yT-1Me"/>
|
||||
<outlet property="delegate" destination="pWR-M4-k2c" id="xKT-ld-5W2"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room name:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VgY-Xe-EWu">
|
||||
<rect key="frame" x="8" y="12" width="91" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. #foo:homeserver)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="cQR-iw-qK7">
|
||||
<rect key="frame" x="109" y="46" width="483" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="wug-Ut-Gbb"/>
|
||||
<outlet property="delegate" destination="pWR-M4-k2c" id="NLj-fO-7S1"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room alias:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Yxi-SY-9AS">
|
||||
<rect key="frame" x="8" y="50" width="83" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="(e.g. @bob:homeserver1; @john:homeserver2...)" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="ntR-iR-LWI">
|
||||
<rect key="frame" x="108" y="84" width="484" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocorrectionType="no" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="editingChanged" id="yLf-bu-2oI"/>
|
||||
<outlet property="delegate" destination="pWR-M4-k2c" id="YMU-bj-YZf"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Participants:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AXJ-7h-Su1">
|
||||
<rect key="frame" x="8" y="87" width="91" height="20"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="8rs-if-WjB">
|
||||
<rect key="frame" x="94" y="125" width="113" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="113" id="586-hY-v29"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="113" id="7dM-UM-eNl"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="113" id="Bjv-y7-dA7"/>
|
||||
</constraints>
|
||||
<segments>
|
||||
<segment title="Public"/>
|
||||
<segment title="Private"/>
|
||||
</segments>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="pWR-M4-k2c" eventType="valueChanged" id="47w-qB-srf"/>
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ega-pA-bej">
|
||||
<rect key="frame" x="411" y="125" width="89" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<state key="normal" title="Create Room">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="onButtonPressed:" destination="pWR-M4-k2c" eventType="touchUpInside" id="gXl-ZF-r1N"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="cQR-iw-qK7" secondAttribute="trailing" constant="8" id="0cB-U1-VZA"/>
|
||||
<constraint firstItem="IVl-ZW-gpm" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="8" id="32v-lV-s4o"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8rs-if-WjB" secondAttribute="centerX" multiplier="2" id="7ZV-PE-7jb"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ntR-iR-LWI" secondAttribute="trailing" constant="8" id="8nG-wm-5IR"/>
|
||||
<constraint firstItem="VgY-Xe-EWu" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="DdS-cu-Y25"/>
|
||||
<constraint firstItem="IVl-ZW-gpm" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="109" id="J5j-Fw-qqc"/>
|
||||
<constraint firstItem="ega-pA-bej" firstAttribute="centerY" secondItem="8rs-if-WjB" secondAttribute="centerY" constant="1" id="Kbc-Bj-YBf"/>
|
||||
<constraint firstItem="cQR-iw-qK7" firstAttribute="centerX" secondItem="IVl-ZW-gpm" secondAttribute="centerX" id="Lai-pI-DCS"/>
|
||||
<constraint firstItem="VgY-Xe-EWu" firstAttribute="centerY" secondItem="IVl-ZW-gpm" secondAttribute="centerY" constant="-1" id="Mls-23-azV"/>
|
||||
<constraint firstAttribute="centerX" secondItem="ega-pA-bej" secondAttribute="centerX" multiplier="0.66" id="Nu0-mz-Cvz"/>
|
||||
<constraint firstItem="ntR-iR-LWI" firstAttribute="centerX" secondItem="IVl-ZW-gpm" secondAttribute="centerX" constant="-0.5" id="OPT-zM-9ST"/>
|
||||
<constraint firstItem="ega-pA-bej" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="125" id="SYc-Ge-iWE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="IVl-ZW-gpm" secondAttribute="trailing" constant="8" id="Scw-Nj-Uef"/>
|
||||
<constraint firstItem="ntR-iR-LWI" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="84" id="ZK5-Z1-Fgq"/>
|
||||
<constraint firstItem="Yxi-SY-9AS" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="cax-9B-Qgo"/>
|
||||
<constraint firstItem="cQR-iw-qK7" firstAttribute="centerY" secondItem="Yxi-SY-9AS" secondAttribute="centerY" constant="1" id="g3m-Mp-LHU"/>
|
||||
<constraint firstItem="ntR-iR-LWI" firstAttribute="centerY" secondItem="AXJ-7h-Su1" secondAttribute="centerY" constant="2" id="suf-qu-Xzx"/>
|
||||
<constraint firstItem="AXJ-7h-Su1" firstAttribute="leading" secondItem="pWR-M4-k2c" secondAttribute="leading" constant="8" id="wwP-0Z-cCe"/>
|
||||
<constraint firstItem="cQR-iw-qK7" firstAttribute="top" secondItem="pWR-M4-k2c" secondAttribute="top" constant="46" id="xMf-E2-suf"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="createRoomBtn" destination="ega-pA-bej" id="psO-nt-6EI"/>
|
||||
<outlet property="createRoomBtnTopConstraint" destination="SYc-Ge-iWE" id="iqw-pV-pK5"/>
|
||||
<outlet property="participantsFieldTopConstraint" destination="ZK5-Z1-Fgq" id="FKM-Yu-PZ0"/>
|
||||
<outlet property="participantsLabel" destination="AXJ-7h-Su1" id="Obs-td-uG8"/>
|
||||
<outlet property="participantsTextField" destination="ntR-iR-LWI" id="vfZ-AE-sPH"/>
|
||||
<outlet property="roomAliasFieldTopConstraint" destination="xMf-E2-suf" id="bgv-5X-sAL"/>
|
||||
<outlet property="roomAliasLabel" destination="Yxi-SY-9AS" id="y2a-fF-YO7"/>
|
||||
<outlet property="roomAliasTextField" destination="cQR-iw-qK7" id="Xmy-Gb-A8S"/>
|
||||
<outlet property="roomNameFieldTopConstraint" destination="32v-lV-s4o" id="Uee-1k-lhQ"/>
|
||||
<outlet property="roomNameLabel" destination="VgY-Xe-EWu" id="36a-Vw-KBs"/>
|
||||
<outlet property="roomNameTextField" destination="IVl-ZW-gpm" id="Q0R-2X-NPw"/>
|
||||
<outlet property="roomVisibilityControl" destination="8rs-if-WjB" id="0bS-Xd-YKE"/>
|
||||
<outlet property="textFieldLeftConstraint" destination="J5j-Fw-qqc" id="5lB-MS-5G6"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="215" y="464"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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/UIKit.h>
|
||||
|
||||
#import "MXKCellData.h"
|
||||
|
||||
/**
|
||||
List the display box types for the cell subviews.
|
||||
*/
|
||||
typedef enum : NSUInteger {
|
||||
/**
|
||||
By default the view display box is unchanged.
|
||||
*/
|
||||
MXKTableViewCellDisplayBoxTypeDefault,
|
||||
/**
|
||||
Define a circle box based on the smaller size of the view frame, some portion of content may be clipped.
|
||||
*/
|
||||
MXKTableViewCellDisplayBoxTypeCircle,
|
||||
/**
|
||||
Round the corner of the display box of the view.
|
||||
*/
|
||||
MXKTableViewCellDisplayBoxTypeRoundedCorner
|
||||
|
||||
} MXKTableViewCellDisplayBoxType;
|
||||
|
||||
/**
|
||||
'MXKTableViewCell' class is used to define custom UITableViewCell.
|
||||
Each 'MXKTableViewCell-inherited' class has its own 'reuseIdentifier'.
|
||||
*/
|
||||
@interface MXKTableViewCell : UITableViewCell
|
||||
{
|
||||
@protected
|
||||
NSString *mxkReuseIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `UINib` object initialized for the cell.
|
||||
|
||||
@return The initialized `UINib` object or `nil` if there were errors during
|
||||
initialization or the nib file could not be located.
|
||||
*/
|
||||
+ (UINib *)nib;
|
||||
|
||||
/**
|
||||
The default reuseIdentifier of the 'MXKTableViewCell-inherited' class.
|
||||
*/
|
||||
+ (NSString*)defaultReuseIdentifier;
|
||||
|
||||
/**
|
||||
Override [UITableViewCell initWithStyle:reuseIdentifier:] to load cell content from nib file (if any),
|
||||
and handle reuse identifier.
|
||||
*/
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier;
|
||||
|
||||
/**
|
||||
Customize the rendering of the table view cell and its subviews (Do nothing by default).
|
||||
This method is called when the view is initialized or prepared for reuse.
|
||||
|
||||
Override this method to customize the table view cell at the application level.
|
||||
*/
|
||||
- (void)customizeTableViewCellRendering;
|
||||
|
||||
/**
|
||||
The current cell data displayed by the table view cell
|
||||
*/
|
||||
@property (weak, nonatomic, readonly) MXKCellData *mxkCellData;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
#import "NSBundle+MatrixKit.h"
|
||||
|
||||
@implementation MXKTableViewCell
|
||||
|
||||
+ (UINib *)nib
|
||||
{
|
||||
// Check whether a nib file is available
|
||||
NSBundle *mainBundle = [NSBundle mxk_bundleForClass:self.class];
|
||||
|
||||
NSString *path = [mainBundle pathForResource:NSStringFromClass([self class]) ofType:@"nib"];
|
||||
if (path)
|
||||
{
|
||||
return [UINib nibWithNibName:NSStringFromClass([self class]) bundle:mainBundle];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSString*)defaultReuseIdentifier
|
||||
{
|
||||
return NSStringFromClass([self class]);
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[super awakeFromNib];
|
||||
|
||||
[self customizeTableViewCellRendering];
|
||||
}
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
|
||||
[self customizeTableViewCellRendering];
|
||||
}
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
// Check whether a xib is defined
|
||||
if ([[self class] nib])
|
||||
{
|
||||
self = [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
[self customizeTableViewCellRendering];
|
||||
}
|
||||
|
||||
if (reuseIdentifier.length)
|
||||
{
|
||||
// The provided identifier is not always conserved in the new created cell.
|
||||
// This depends how the method [initWithStyle:reuseIdentifier:] is trigerred.
|
||||
// Trick: we store a copy of this identifier.
|
||||
mxkReuseIdentifier = reuseIdentifier;
|
||||
}
|
||||
else
|
||||
{
|
||||
mxkReuseIdentifier = [[self class] defaultReuseIdentifier];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString*)reuseIdentifier
|
||||
{
|
||||
NSString *identifier = super.reuseIdentifier;
|
||||
|
||||
if (!identifier.length)
|
||||
{
|
||||
identifier = mxkReuseIdentifier;
|
||||
}
|
||||
|
||||
return identifier;
|
||||
}
|
||||
|
||||
- (void)customizeTableViewCellRendering
|
||||
{
|
||||
// Do nothing by default.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithButton' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UIButton' centered in cell content view.
|
||||
*/
|
||||
@interface MXKTableViewCellWithButton : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithButton.h"
|
||||
|
||||
@implementation MXKTableViewCellWithButton
|
||||
|
||||
- (void)prepareForReuse
|
||||
{
|
||||
[super prepareForReuse];
|
||||
|
||||
// TODO: Code commented for a quick fix for https://github.com/vector-im/riot-ios/issues/1323
|
||||
// This line was a fix for https://github.com/vector-im/riot-ios/issues/1354
|
||||
// but it creates a regression that is worse than the bug it fixes.
|
||||
// self.mxkButton.titleLabel.text = nil;
|
||||
|
||||
[self.mxkButton removeTarget:nil action:nil forControlEvents:UIControlEventAllEvents];
|
||||
self.mxkButton.accessibilityIdentifier = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<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="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ycy-33-7Zq">
|
||||
<rect key="frame" x="276" y="7" width="49" height="30"/>
|
||||
<state key="normal" title="Submit">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="Ycy-33-7Zq" secondAttribute="centerY" id="6kh-Pa-47V"/>
|
||||
<constraint firstAttribute="centerX" secondItem="Ycy-33-7Zq" secondAttribute="centerX" id="uAj-IK-F0I"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkButton" destination="Ycy-33-7Zq" id="rQt-Th-xOb"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithButtons' inherits 'MXKTableViewCell' class.
|
||||
It displays several buttons with the system style in a UITableViewCell. All buttons have the same width and they are horizontally aligned.
|
||||
They are vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithButtons : MXKTableViewCell
|
||||
|
||||
/**
|
||||
The number of buttons
|
||||
*/
|
||||
@property (nonatomic) NSUInteger mxkButtonNumber;
|
||||
|
||||
/**
|
||||
The current array of buttons
|
||||
*/
|
||||
@property (nonatomic) NSArray *mxkButtons;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithButtons.h"
|
||||
|
||||
@interface MXKTableViewCellWithButtons ()
|
||||
{
|
||||
NSMutableArray *buttonArray;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation MXKTableViewCellWithButtons
|
||||
|
||||
- (void)setMxkButtonNumber:(NSUInteger)buttonNumber
|
||||
{
|
||||
if (_mxkButtonNumber == buttonNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_mxkButtonNumber = buttonNumber;
|
||||
buttonArray = [NSMutableArray arrayWithCapacity:buttonNumber];
|
||||
|
||||
CGFloat containerWidth = self.contentView.frame.size.width / buttonNumber;
|
||||
UIView *previousContainer = nil;
|
||||
NSLayoutConstraint *leftConstraint;
|
||||
NSLayoutConstraint *rightConstraint;
|
||||
NSLayoutConstraint *widthConstraint;
|
||||
NSLayoutConstraint *topConstraint;
|
||||
NSLayoutConstraint *bottomConstraint;
|
||||
|
||||
for (NSInteger index = 0; index < buttonNumber; index++)
|
||||
{
|
||||
UIView *buttonContainer = [[UIView alloc] initWithFrame:CGRectMake(index * containerWidth, 0, containerWidth, self.contentView.frame.size.height)];
|
||||
buttonContainer.backgroundColor = [UIColor clearColor];
|
||||
[self.contentView addSubview:buttonContainer];
|
||||
|
||||
// Add container constraints
|
||||
buttonContainer.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
if (!previousContainer)
|
||||
{
|
||||
leftConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeLeading
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.contentView
|
||||
attribute:NSLayoutAttributeLeading
|
||||
multiplier:1
|
||||
constant:0];
|
||||
widthConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.contentView
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:(1.0 / buttonNumber)
|
||||
constant:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
leftConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeLeading
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:previousContainer
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1
|
||||
constant:0];
|
||||
widthConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeWidth
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:previousContainer
|
||||
attribute:NSLayoutAttributeWidth
|
||||
multiplier:1
|
||||
constant:0];
|
||||
}
|
||||
|
||||
topConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.contentView
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1
|
||||
constant:0];
|
||||
|
||||
bottomConstraint = [NSLayoutConstraint constraintWithItem:buttonContainer
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:self.contentView
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1
|
||||
constant:0];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[leftConstraint, widthConstraint, topConstraint, bottomConstraint]];
|
||||
previousContainer = buttonContainer;
|
||||
|
||||
// Add Button
|
||||
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
|
||||
button.frame = CGRectMake(10, 8, containerWidth - 20, buttonContainer.frame.size.height - 16);
|
||||
[buttonContainer addSubview:button];
|
||||
[buttonArray addObject:button];
|
||||
|
||||
// Add button constraints
|
||||
button.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
leftConstraint = [NSLayoutConstraint constraintWithItem:button
|
||||
attribute:NSLayoutAttributeLeading
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:buttonContainer
|
||||
attribute:NSLayoutAttributeLeading
|
||||
multiplier:1
|
||||
constant:10];
|
||||
rightConstraint = [NSLayoutConstraint constraintWithItem:button
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:buttonContainer
|
||||
attribute:NSLayoutAttributeTrailing
|
||||
multiplier:1
|
||||
constant:-10];
|
||||
|
||||
topConstraint = [NSLayoutConstraint constraintWithItem:button
|
||||
attribute:NSLayoutAttributeTop
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:buttonContainer
|
||||
attribute:NSLayoutAttributeTop
|
||||
multiplier:1
|
||||
constant:8];
|
||||
|
||||
bottomConstraint = [NSLayoutConstraint constraintWithItem:button
|
||||
attribute:NSLayoutAttributeBottom
|
||||
relatedBy:NSLayoutRelationEqual
|
||||
toItem:buttonContainer
|
||||
attribute:NSLayoutAttributeBottom
|
||||
multiplier:1
|
||||
constant:-8];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, bottomConstraint]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSArray*)mxkButtons
|
||||
{
|
||||
return [NSArray arrayWithArray:buttonArray];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndButton' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'UIButton' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndButton : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonProportionalWidthToMxkButtonConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndButton.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndButton
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
|
||||
<rect key="frame" x="8" y="11" width="411" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nTw-zr-xt2">
|
||||
<rect key="frame" x="427" y="7" width="165" height="30"/>
|
||||
<state key="normal" title="Button">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="A2R-gN-0sW"/>
|
||||
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
|
||||
<constraint firstAttribute="trailing" secondItem="nTw-zr-xt2" secondAttribute="trailing" constant="8" id="f4b-wS-uFy"/>
|
||||
<constraint firstAttribute="centerY" secondItem="nTw-zr-xt2" secondAttribute="centerY" id="gyq-nC-R4z"/>
|
||||
<constraint firstItem="nTw-zr-xt2" firstAttribute="width" secondItem="Xx4-AP-OQs" secondAttribute="width" multiplier="0.4" id="ill-js-N8j"/>
|
||||
<constraint firstItem="nTw-zr-xt2" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="s8d-Y5-xZR"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkButton" destination="nTw-zr-xt2" id="I8D-FA-NHu"/>
|
||||
<outlet property="mxkButtonLeadingConstraint" destination="s8d-Y5-xZR" id="yFz-3Q-x4B"/>
|
||||
<outlet property="mxkButtonProportionalWidthToMxkButtonConstraint" destination="ill-js-N8j" id="XNz-6f-8V1"/>
|
||||
<outlet property="mxkButtonTrailingConstraint" destination="f4b-wS-uFy" id="W7t-7z-PeJ"/>
|
||||
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="LI1-Yq-PW8"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="2dO-ba-i74"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndImageView' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'UIImageView' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndImageView : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UIImageView *mxkImageView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewWidthConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewHeightConstraint;
|
||||
|
||||
/**
|
||||
The image view display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
|
||||
*/
|
||||
@property (nonatomic) MXKTableViewCellDisplayBoxType mxkImageViewDisplayBoxType;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndImageView.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndImageView
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
|
||||
{
|
||||
// Round image view for thumbnail
|
||||
_mxkImageView.layer.cornerRadius = _mxkImageView.frame.size.width / 2;
|
||||
_mxkImageView.clipsToBounds = YES;
|
||||
}
|
||||
else if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
|
||||
{
|
||||
_mxkImageView.layer.cornerRadius = 5;
|
||||
_mxkImageView.clipsToBounds = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mxkImageView.layer.cornerRadius = 0;
|
||||
_mxkImageView.clipsToBounds = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
|
||||
<rect key="frame" x="8" y="11" width="543" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="F9Q-h1-mTt">
|
||||
<rect key="frame" x="559" y="5" width="33" height="33"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="33" id="Mbb-3z-EJx"/>
|
||||
<constraint firstAttribute="height" constant="33" id="SN2-B2-NSE"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="F9Q-h1-mTt" firstAttribute="centerY" secondItem="Eg5-vl-rni" secondAttribute="centerY" id="2I6-r3-K1d"/>
|
||||
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
|
||||
<constraint firstItem="F9Q-h1-mTt" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" priority="750" constant="8" id="mXu-V2-6Sm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="F9Q-h1-mTt" secondAttribute="trailing" constant="8" id="v4T-uD-LAm"/>
|
||||
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="v6O-QC-moW"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkImageView" destination="F9Q-h1-mTt" id="4Lx-au-5RN"/>
|
||||
<outlet property="mxkImageViewHeightConstraint" destination="SN2-B2-NSE" id="Zql-LZ-nsj"/>
|
||||
<outlet property="mxkImageViewLeadingConstraint" destination="mXu-V2-6Sm" id="Ecf-aj-MJv"/>
|
||||
<outlet property="mxkImageViewTrailingConstraint" destination="v4T-uD-LAm" id="HLK-AJ-Gj9"/>
|
||||
<outlet property="mxkImageViewWidthConstraint" destination="Mbb-3z-EJx" id="10m-q5-Yok"/>
|
||||
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="PHz-Lv-lrf"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="tyK-6D-jiQ"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
#import "MXKImageView.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndMXKImageView' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'MXKImageView' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndMXKImageView : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet MXKImageView *mxkImageView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewWidthConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkImageViewHeightConstraint;
|
||||
|
||||
/**
|
||||
The MXKImageView display box type ('MXKTableViewCellDisplayBoxTypeDefault' by default)
|
||||
*/
|
||||
@property (nonatomic) MXKTableViewCellDisplayBoxType mxkImageViewDisplayBoxType;
|
||||
|
||||
@end
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndMXKImageView.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndMXKImageView
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeCircle)
|
||||
{
|
||||
// Round image view for thumbnail
|
||||
_mxkImageView.layer.cornerRadius = _mxkImageView.frame.size.width / 2;
|
||||
_mxkImageView.clipsToBounds = YES;
|
||||
}
|
||||
else if (self.mxkImageViewDisplayBoxType == MXKTableViewCellDisplayBoxTypeRoundedCorner)
|
||||
{
|
||||
_mxkImageView.layer.cornerRadius = 5;
|
||||
_mxkImageView.clipsToBounds = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
_mxkImageView.layer.cornerRadius = 0;
|
||||
_mxkImageView.clipsToBounds = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndMXKImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
|
||||
<rect key="frame" x="8" y="11" width="538" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Ts-eR-MZV" userLabel="Mxk ImageView" customClass="MXKImageView">
|
||||
<rect key="frame" x="554" y="5" width="33" height="33"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="33" id="Ejk-bJ-0MB"/>
|
||||
<constraint firstAttribute="height" constant="33" id="m9S-XK-OTa"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
|
||||
<constraint firstAttribute="trailing" secondItem="7Ts-eR-MZV" secondAttribute="trailing" constant="13" id="atK-Pp-aD8"/>
|
||||
<constraint firstItem="7Ts-eR-MZV" firstAttribute="leading" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="mm9-sU-77x"/>
|
||||
<constraint firstAttribute="centerY" secondItem="Xx4-AP-OQs" secondAttribute="centerY" id="v6O-QC-moW"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<constraints>
|
||||
<constraint firstItem="7Ts-eR-MZV" firstAttribute="centerY" secondItem="aCf-VI-ocb" secondAttribute="centerY" id="lgB-TU-fX3"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="mxkImageView" destination="7Ts-eR-MZV" id="ALS-2d-HUG"/>
|
||||
<outlet property="mxkImageViewHeightConstraint" destination="m9S-XK-OTa" id="hRs-A7-Zrx"/>
|
||||
<outlet property="mxkImageViewLeadingConstraint" destination="mm9-sU-77x" id="nNv-Ns-zdz"/>
|
||||
<outlet property="mxkImageViewTrailingConstraint" destination="atK-Pp-aD8" id="kr5-ot-BDV"/>
|
||||
<outlet property="mxkImageViewWidthConstraint" destination="Ejk-bJ-0MB" id="fgn-XC-e4o"/>
|
||||
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="PHz-Lv-lrf"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="ONt-IX-CEx"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndSlider' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'UISlider'.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndSlider : MXKTableViewCell
|
||||
|
||||
@property (nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (nonatomic) IBOutlet UISlider *mxkSlider;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderTrailingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderBottomConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSliderHeightConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndSlider.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndSlider
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="88" id="lA9-3N-IIx" customClass="MXKTableViewCellWithLabelAndSlider">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="88"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="lA9-3N-IIx" id="Vc0-lB-zV6">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="87"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="k24-l4-6rr">
|
||||
<rect key="frame" x="8" y="8" width="584" height="34"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="34" id="FNq-mS-rjp"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="gcY-FQ-8r8">
|
||||
<rect key="frame" x="6" y="49" width="588" height="31"/>
|
||||
<color key="minimumTrackTintColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="maximumTrackTintColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</slider>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="k24-l4-6rr" firstAttribute="top" secondItem="Vc0-lB-zV6" secondAttribute="top" constant="8" id="2hw-Fl-Xrg"/>
|
||||
<constraint firstAttribute="bottom" secondItem="gcY-FQ-8r8" secondAttribute="bottom" constant="8" id="9Om-Ak-gtM"/>
|
||||
<constraint firstItem="gcY-FQ-8r8" firstAttribute="leading" secondItem="Vc0-lB-zV6" secondAttribute="leading" constant="8" id="FkG-hh-eyX"/>
|
||||
<constraint firstItem="k24-l4-6rr" firstAttribute="leading" secondItem="Vc0-lB-zV6" secondAttribute="leading" constant="8" id="OLf-mB-0l0"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k24-l4-6rr" secondAttribute="trailing" constant="8" id="PFM-WC-USa"/>
|
||||
<constraint firstItem="gcY-FQ-8r8" firstAttribute="top" secondItem="k24-l4-6rr" secondAttribute="bottom" constant="7" id="aEh-bN-jLX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gcY-FQ-8r8" secondAttribute="trailing" constant="8" id="mo8-a8-N8Q"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outlet property="mxkLabel" destination="k24-l4-6rr" id="y0D-Mw-0um"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="OLf-mB-0l0" id="ET4-n2-A2u"/>
|
||||
<outlet property="mxkLabelTopConstraint" destination="2hw-Fl-Xrg" id="t7n-aA-lql"/>
|
||||
<outlet property="mxkLabelTrailingConstraint" destination="PFM-WC-USa" id="MPu-eZ-Utj"/>
|
||||
<outlet property="mxkSlider" destination="gcY-FQ-8r8" id="uhT-97-E4d"/>
|
||||
<outlet property="mxkSliderBottomConstraint" destination="9Om-Ak-gtM" id="A7D-Tr-5Xg"/>
|
||||
<outlet property="mxkSliderHeightConstraint" destination="FNq-mS-rjp" id="0vO-0U-iqS"/>
|
||||
<outlet property="mxkSliderLeadingConstraint" destination="FkG-hh-eyX" id="gCG-SE-H49"/>
|
||||
<outlet property="mxkSliderTopConstraint" destination="aEh-bN-jLX" id="6Y5-0U-7Ma"/>
|
||||
<outlet property="mxkSliderTrailingConstraint" destination="mo8-a8-N8Q" id="G8j-DF-rZr"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndSubLabel' inherits 'MXKTableViewCell' class.
|
||||
It constains two 'UILabel' instances.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndSubLabel : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel* mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel* mxkSublabel;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelTrailingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSublabelBottomConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndSubLabel.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndSubLabel
|
||||
|
||||
@end
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="odA-la-SwE" customClass="MXKTableViewCellWithLabelAndSubLabel">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="odA-la-SwE" id="Ice-YN-Ffy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rfi-Fa-7Uq">
|
||||
<rect key="frame" x="8" y="6" width="584" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sublabel" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bFk-PP-KQ8">
|
||||
<rect key="frame" x="8" y="22" width="584" height="15"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="bFk-PP-KQ8" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="8" id="EIU-NZ-lLo"/>
|
||||
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="top" secondItem="Ice-YN-Ffy" secondAttribute="top" constant="6" id="RhT-OU-fAa"/>
|
||||
<constraint firstAttribute="bottom" secondItem="bFk-PP-KQ8" secondAttribute="bottom" constant="6" id="XUy-p8-Cn5"/>
|
||||
<constraint firstItem="rfi-Fa-7Uq" firstAttribute="leading" secondItem="Ice-YN-Ffy" secondAttribute="leading" constant="8" id="czr-je-LwV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="rfi-Fa-7Uq" secondAttribute="trailing" constant="8" id="pnn-m6-Idk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bFk-PP-KQ8" secondAttribute="trailing" constant="8" id="tlW-4P-80L"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkLabel" destination="rfi-Fa-7Uq" id="F0V-zy-oHR"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="czr-je-LwV" id="RiL-Rk-lx8"/>
|
||||
<outlet property="mxkLabelTopConstraint" destination="RhT-OU-fAa" id="tDf-zZ-FDk"/>
|
||||
<outlet property="mxkLabelTrailingConstraint" destination="pnn-m6-Idk" id="fXH-jC-dc5"/>
|
||||
<outlet property="mxkSublabel" destination="bFk-PP-KQ8" id="w63-Pz-7Fc"/>
|
||||
<outlet property="mxkSublabelBottomConstraint" destination="XUy-p8-Cn5" id="ula-Yb-yTz"/>
|
||||
<outlet property="mxkSublabelLeadingConstraint" destination="EIU-NZ-lLo" id="KF2-oE-8yn"/>
|
||||
<outlet property="mxkSublabelTrailingConstraint" destination="tlW-4P-80L" id="uP5-iq-D1f"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndSwitch' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'UISwitch' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndSwitch : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UISwitch *mxkSwitch;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSwitchLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSwitchTrailingConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndSwitch.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndSwitch
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="12121" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="aCf-VI-ocb" customClass="MXKTableViewCellWithLabelAndSwitch">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="aCf-VI-ocb" id="Eg5-vl-rni">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9dA-E8-w3v">
|
||||
<rect key="frame" x="543" y="6" width="51" height="31"/>
|
||||
</switch>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xx4-AP-OQs">
|
||||
<rect key="frame" x="8" y="11" width="42" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="9dA-E8-w3v" firstAttribute="centerY" secondItem="Eg5-vl-rni" secondAttribute="centerY" id="HHv-PY-RDK"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="Xx4-AP-OQs" secondAttribute="bottom" constant="3" id="Ikz-iJ-l4h"/>
|
||||
<constraint firstItem="Xx4-AP-OQs" firstAttribute="top" secondItem="Eg5-vl-rni" secondAttribute="topMargin" constant="3" id="Vem-3Z-NyP"/>
|
||||
<constraint firstItem="Xx4-AP-OQs" firstAttribute="leading" secondItem="Eg5-vl-rni" secondAttribute="leading" constant="8" id="Wih-ke-1dd"/>
|
||||
<constraint firstItem="9dA-E8-w3v" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Xx4-AP-OQs" secondAttribute="trailing" constant="8" id="hms-8a-BYd"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9dA-E8-w3v" secondAttribute="trailing" constant="8" id="oN3-Ph-hS5"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkLabel" destination="Xx4-AP-OQs" id="jTM-zE-gtP"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="Wih-ke-1dd" id="b2U-OW-efL"/>
|
||||
<outlet property="mxkSwitch" destination="9dA-E8-w3v" id="sko-Dh-EsF"/>
|
||||
<outlet property="mxkSwitchLeadingConstraint" destination="hms-8a-BYd" id="ACm-RB-bE8"/>
|
||||
<outlet property="mxkSwitchTrailingConstraint" destination="oN3-Ph-hS5" id="Y17-Ii-c3Y"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelAndTextField' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' and a 'UITextField' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelAndTextField : MXKTableViewCell <UITextFieldDelegate>
|
||||
{
|
||||
@protected
|
||||
UIView *inputAccessoryView;
|
||||
}
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
|
||||
|
||||
/**
|
||||
The custom accessory view associated with the text field. This view is
|
||||
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
|
||||
the accessory view when the text field become the first responder.
|
||||
*/
|
||||
@property (readonly) UIView *inputAccessoryView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelMinWidthConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldTrailingConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelAndTextField.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelAndTextField
|
||||
@synthesize inputAccessoryView;
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self)
|
||||
{
|
||||
// Add an accessory view to the text view in order to retrieve keyboard view.
|
||||
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_mxkTextField.inputAccessoryView = inputAccessoryView;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
inputAccessoryView = nil;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
// Fix the minimum width of the label in order to keep it visible when the textfield width is increasing.
|
||||
[_mxkLabel sizeToFit];
|
||||
_mxkLabelMinWidthConstraint.constant = _mxkLabel.frame.size.width;
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[self.mxkTextField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="5Je-1y-foH" customClass="MXKTableViewCellWithLabelAndTextField">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="5Je-1y-foH" id="hCm-wd-Yup">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="14" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="SBE-PS-i6p">
|
||||
<rect key="frame" x="58" y="7" width="534" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="done"/>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="5Je-1y-foH" id="Nie-b5-gB9"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="584" translatesAutoresizingMaskIntoConstraints="NO" id="DE7-uC-Oz2">
|
||||
<rect key="frame" x="8" y="11" width="42" height="21"/>
|
||||
<color key="tintColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="10" id="Qzq-Ej-Wdb"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="SBE-PS-i6p" secondAttribute="trailing" constant="8" id="7NM-6p-4fc"/>
|
||||
<constraint firstItem="SBE-PS-i6p" firstAttribute="centerY" secondItem="DE7-uC-Oz2" secondAttribute="centerY" id="LEG-PI-Zmz"/>
|
||||
<constraint firstAttribute="centerY" secondItem="DE7-uC-Oz2" secondAttribute="centerY" id="PU3-ZA-23J"/>
|
||||
<constraint firstItem="SBE-PS-i6p" firstAttribute="leading" secondItem="DE7-uC-Oz2" secondAttribute="trailing" constant="8" id="h1v-HS-kzp"/>
|
||||
<constraint firstItem="DE7-uC-Oz2" firstAttribute="leading" secondItem="hCm-wd-Yup" secondAttribute="leading" constant="8" id="qse-r7-x6e"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkLabel" destination="DE7-uC-Oz2" id="CTb-my-hkV"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="qse-r7-x6e" id="WiE-p0-3PB"/>
|
||||
<outlet property="mxkLabelMinWidthConstraint" destination="Qzq-Ej-Wdb" id="SJc-IC-N5T"/>
|
||||
<outlet property="mxkTextField" destination="SBE-PS-i6p" id="jLi-27-mbO"/>
|
||||
<outlet property="mxkTextFieldLeadingConstraint" destination="h1v-HS-kzp" id="Sx7-JU-SeG"/>
|
||||
<outlet property="mxkTextFieldTrailingConstraint" destination="7NM-6p-4fc" id="tBw-Gt-9pm"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithLabelTextFieldAndButton' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UILabel' on the first line. The second line is composed with a 'UITextField' and a 'UIButton'
|
||||
vertically aligned.
|
||||
*/
|
||||
@interface MXKTableViewCellWithLabelTextFieldAndButton : MXKTableViewCell <UITextFieldDelegate>
|
||||
{
|
||||
@protected
|
||||
UIView *inputAccessoryView;
|
||||
}
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UILabel *mxkLabel;
|
||||
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
|
||||
|
||||
/**
|
||||
The custom accessory view associated with the text field. This view is
|
||||
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
|
||||
the accessory view when the text field become the first responder.
|
||||
*/
|
||||
@property (readonly) UIView *inputAccessoryView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkLabelTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldBottomConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonMinWidthConstraint;
|
||||
|
||||
- (IBAction)textFieldEditingChanged:(id)sender;
|
||||
|
||||
@end
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithLabelTextFieldAndButton.h"
|
||||
|
||||
@implementation MXKTableViewCellWithLabelTextFieldAndButton
|
||||
@synthesize inputAccessoryView;
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self)
|
||||
{
|
||||
// Add an accessory view to the text view in order to retrieve keyboard view.
|
||||
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_mxkTextField.inputAccessoryView = inputAccessoryView;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
inputAccessoryView = nil;
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[self.mxkTextField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Action
|
||||
|
||||
- (IBAction)textFieldEditingChanged:(id)sender
|
||||
{
|
||||
self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" rowHeight="70" id="R1k-d9-UZq" customClass="MXKTableViewCellWithLabelTextFieldAndButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="70"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="R1k-d9-UZq" id="TrF-Sd-htc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="69"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="HwF-3u-e5Q">
|
||||
<rect key="frame" x="8" y="33" width="527" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="R1k-d9-UZq" eventType="editingChanged" id="LeA-rJ-pcV"/>
|
||||
<outlet property="delegate" destination="R1k-d9-UZq" id="X6b-0x-2HJ"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KoB-y6-MAj">
|
||||
<rect key="frame" x="543" y="33" width="49" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="8Yl-vg-asf"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Submit">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="14" translatesAutoresizingMaskIntoConstraints="NO" id="9Ez-M7-s8I">
|
||||
<rect key="frame" x="8" y="8" width="584" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="KoB-y6-MAj" firstAttribute="centerY" secondItem="HwF-3u-e5Q" secondAttribute="centerY" id="0j3-Pg-l0f"/>
|
||||
<constraint firstAttribute="trailing" secondItem="KoB-y6-MAj" secondAttribute="trailing" constant="8" id="Bed-gz-IoV"/>
|
||||
<constraint firstItem="9Ez-M7-s8I" firstAttribute="top" secondItem="TrF-Sd-htc" secondAttribute="top" constant="8" id="UiT-hp-y64"/>
|
||||
<constraint firstAttribute="bottom" secondItem="HwF-3u-e5Q" secondAttribute="bottom" constant="6" id="VBI-N7-ubO"/>
|
||||
<constraint firstAttribute="trailing" secondItem="9Ez-M7-s8I" secondAttribute="trailing" constant="8" id="cKm-EC-j6C"/>
|
||||
<constraint firstItem="HwF-3u-e5Q" firstAttribute="top" secondItem="9Ez-M7-s8I" secondAttribute="bottom" constant="4" id="hsT-zv-aFU"/>
|
||||
<constraint firstItem="HwF-3u-e5Q" firstAttribute="leading" secondItem="TrF-Sd-htc" secondAttribute="leading" constant="8" id="uYR-ZY-FTK"/>
|
||||
<constraint firstItem="KoB-y6-MAj" firstAttribute="leading" secondItem="HwF-3u-e5Q" secondAttribute="trailing" constant="8" id="vZw-2I-IYg"/>
|
||||
<constraint firstItem="9Ez-M7-s8I" firstAttribute="leading" secondItem="TrF-Sd-htc" secondAttribute="leading" constant="8" id="z1S-ho-Niu"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkButton" destination="KoB-y6-MAj" id="HtV-Hu-sJB"/>
|
||||
<outlet property="mxkButtonLeadingConstraint" destination="vZw-2I-IYg" id="tSD-e8-387"/>
|
||||
<outlet property="mxkButtonMinWidthConstraint" destination="8Yl-vg-asf" id="Ysh-Vs-QYO"/>
|
||||
<outlet property="mxkButtonTrailingConstraint" destination="Bed-gz-IoV" id="3KC-1v-buQ"/>
|
||||
<outlet property="mxkLabel" destination="9Ez-M7-s8I" id="RAD-Qz-GHK"/>
|
||||
<outlet property="mxkLabelLeadingConstraint" destination="z1S-ho-Niu" id="nPj-Hs-eA6"/>
|
||||
<outlet property="mxkLabelTopConstraint" destination="UiT-hp-y64" id="Fq9-DJ-zQN"/>
|
||||
<outlet property="mxkLabelTrailingConstraint" destination="cKm-EC-j6C" id="nA3-he-Wll"/>
|
||||
<outlet property="mxkTextField" destination="HwF-3u-e5Q" id="aTc-9G-9BE"/>
|
||||
<outlet property="mxkTextFieldBottomConstraint" destination="VBI-N7-ubO" id="iwS-Hp-9EQ"/>
|
||||
<outlet property="mxkTextFieldLeadingConstraint" destination="uYR-ZY-FTK" id="kOH-F3-xGk"/>
|
||||
<outlet property="mxkTextFieldTopConstraint" destination="hsT-zv-aFU" id="n5u-Lo-TOd"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithPicker' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UIPickerView' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithPicker : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIPickerView* mxkPickerView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewBottomConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkPickerViewTrailingConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithPicker.h"
|
||||
|
||||
@implementation MXKTableViewCellWithPicker
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" rowHeight="164" id="q82-Um-cU7" customClass="MXKTableViewCellWithPicker">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q82-Um-cU7" id="aBM-uR-U5E">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="163"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hrr-rd-eoA">
|
||||
<rect key="frame" x="8" y="8" width="584" height="147"/>
|
||||
</pickerView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="hrr-rd-eoA" firstAttribute="top" secondItem="aBM-uR-U5E" secondAttribute="top" constant="8" id="J6o-ID-bDs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="hrr-rd-eoA" secondAttribute="trailing" constant="8" id="SSs-GG-jvP"/>
|
||||
<constraint firstItem="hrr-rd-eoA" firstAttribute="leading" secondItem="aBM-uR-U5E" secondAttribute="leading" constant="8" id="V0R-Eb-PWo"/>
|
||||
<constraint firstAttribute="bottom" secondItem="hrr-rd-eoA" secondAttribute="bottom" constant="8" id="h2E-4o-qec"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkPickerView" destination="hrr-rd-eoA" id="HBG-aN-sZg"/>
|
||||
<outlet property="mxkPickerViewBottomConstraint" destination="h2E-4o-qec" id="2eo-oT-tUZ"/>
|
||||
<outlet property="mxkPickerViewLeadingConstraint" destination="V0R-Eb-PWo" id="uBh-Jo-oOn"/>
|
||||
<outlet property="mxkPickerViewTopConstraint" destination="J6o-ID-bDs" id="pEK-EQ-1IJ"/>
|
||||
<outlet property="mxkPickerViewTrailingConstraint" destination="SSs-GG-jvP" id="8JI-Zy-LfB"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithSearchBar' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UISearchBar' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithSearchBar : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UISearchBar *mxkSearchBar;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarBottomConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkSearchBarTrailingConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithSearchBar.h"
|
||||
|
||||
@implementation MXKTableViewCellWithSearchBar
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithSearchBar">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<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="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<searchBar contentMode="redraw" translatesAutoresizingMaskIntoConstraints="NO" id="HgP-TQ-bd1">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</searchBar>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="HgP-TQ-bd1" secondAttribute="bottom" id="9RG-MY-amb"/>
|
||||
<constraint firstItem="HgP-TQ-bd1" firstAttribute="top" secondItem="cFw-g7-Cgn" secondAttribute="top" id="csH-4b-32a"/>
|
||||
<constraint firstItem="HgP-TQ-bd1" firstAttribute="leading" secondItem="cFw-g7-Cgn" secondAttribute="leading" id="duA-9h-WEA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="HgP-TQ-bd1" secondAttribute="trailing" id="y0I-NU-96o"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkSearchBar" destination="HgP-TQ-bd1" id="2YT-dI-X4U"/>
|
||||
<outlet property="mxkSearchBarBottomConstraint" destination="9RG-MY-amb" id="CFM-k7-uGe"/>
|
||||
<outlet property="mxkSearchBarLeadingConstraint" destination="duA-9h-WEA" id="5vm-V4-Qsu"/>
|
||||
<outlet property="mxkSearchBarTopConstraint" destination="csH-4b-32a" id="NHJ-WR-9Hm"/>
|
||||
<outlet property="mxkSearchBarTrailingConstraint" destination="y0I-NU-96o" id="lb1-cE-sE0"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithTextFieldAndButton' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UITextField' and a 'UIButton' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithTextFieldAndButton : MXKTableViewCell <UITextFieldDelegate>
|
||||
{
|
||||
@protected
|
||||
UIView *inputAccessoryView;
|
||||
}
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITextField *mxkTextField;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *mxkButton;
|
||||
|
||||
/**
|
||||
The custom accessory view associated with the text field. This view is
|
||||
actually used to retrieve the keyboard view. Indeed the keyboard view is the superview of
|
||||
the accessory view when the text field become the first responder.
|
||||
*/
|
||||
@property (readonly) UIView *inputAccessoryView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextFieldLeadingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonTrailingConstraint;
|
||||
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkButtonMinWidthConstraint;
|
||||
|
||||
- (IBAction)textFieldEditingChanged:(id)sender;
|
||||
@end
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithTextFieldAndButton.h"
|
||||
|
||||
@implementation MXKTableViewCellWithTextFieldAndButton
|
||||
@synthesize inputAccessoryView;
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
|
||||
{
|
||||
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
|
||||
if (self)
|
||||
{
|
||||
// Add an accessory view to the text view in order to retrieve keyboard view.
|
||||
inputAccessoryView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_mxkTextField.inputAccessoryView = inputAccessoryView;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
inputAccessoryView = nil;
|
||||
}
|
||||
|
||||
#pragma mark - UITextField delegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField*)textField
|
||||
{
|
||||
// "Done" key has been pressed
|
||||
[self.mxkTextField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Action
|
||||
|
||||
- (IBAction)textFieldEditingChanged:(id)sender
|
||||
{
|
||||
self.mxkButton.enabled = (self.mxkTextField.text.length != 0);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
||||
<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"/>
|
||||
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="20U-7H-xmi" customClass="MXKTableViewCellWithTextFieldAndButton">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<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="43"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="XWh-rM-iqf">
|
||||
<rect key="frame" x="8" y="7" width="527" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" returnKeyType="done"/>
|
||||
<connections>
|
||||
<action selector="textFieldEditingChanged:" destination="20U-7H-xmi" eventType="editingChanged" id="XUA-5O-AC8"/>
|
||||
<outlet property="delegate" destination="20U-7H-xmi" id="IIR-M2-klm"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ycy-33-7Zq">
|
||||
<rect key="frame" x="543" y="7" width="49" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="bvf-cL-4kz"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Submit">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Ycy-33-7Zq" secondAttribute="trailing" constant="8" id="PaG-vi-8XK"/>
|
||||
<constraint firstAttribute="centerY" secondItem="XWh-rM-iqf" secondAttribute="centerY" id="TMe-P5-cPy"/>
|
||||
<constraint firstAttribute="centerY" secondItem="Ycy-33-7Zq" secondAttribute="centerY" id="WRy-Ga-ivi"/>
|
||||
<constraint firstItem="XWh-rM-iqf" firstAttribute="leading" secondItem="cFw-g7-Cgn" secondAttribute="leading" constant="8" id="Ydc-ON-VIq"/>
|
||||
<constraint firstItem="Ycy-33-7Zq" firstAttribute="leading" secondItem="XWh-rM-iqf" secondAttribute="trailing" constant="8" id="vw8-zU-VJt"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="mxkButton" destination="Ycy-33-7Zq" id="fwp-cx-faQ"/>
|
||||
<outlet property="mxkButtonLeadingConstraint" destination="vw8-zU-VJt" id="Lep-jf-4Sa"/>
|
||||
<outlet property="mxkButtonMinWidthConstraint" destination="bvf-cL-4kz" id="Q0x-ro-SjH"/>
|
||||
<outlet property="mxkButtonTrailingConstraint" destination="PaG-vi-8XK" id="fYB-xQ-ppJ"/>
|
||||
<outlet property="mxkTextField" destination="XWh-rM-iqf" id="2Bm-Ks-lAr"/>
|
||||
<outlet property="mxkTextFieldLeadingConstraint" destination="Ydc-ON-VIq" id="Jap-J5-Vno"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCell.h"
|
||||
|
||||
/**
|
||||
'MXKTableViewCellWithTextView' inherits 'MXKTableViewCell' class.
|
||||
It constains a 'UITextView' vertically centered.
|
||||
*/
|
||||
@interface MXKTableViewCellWithTextView : MXKTableViewCell
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITextView *mxkTextView;
|
||||
|
||||
/**
|
||||
Leading/Trailing constraints define here spacing to nearest neighbor (no relative to margin)
|
||||
*/
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewLeadingConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewTopConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewBottomConstraint;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mxkTextViewTrailingConstraint;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
Copyright 2015 OpenMarket 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 "MXKTableViewCellWithTextView.h"
|
||||
|
||||
@implementation MXKTableViewCellWithTextView
|
||||
|
||||
@end
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user