Merge branch 'master' into development
|
After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@@ -3,109 +3,109 @@
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-20@2x.png",
|
||||
"filename" : "30243544-20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-20@3x.png",
|
||||
"filename" : "30243544-20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-29@2x.png",
|
||||
"filename" : "30243544-29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-29@3x.png",
|
||||
"filename" : "30243544-29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-40@2x.png",
|
||||
"filename" : "30243544-40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-40@3x.png",
|
||||
"filename" : "30243544-40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-60@2x.png",
|
||||
"filename" : "30243544-60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "mklogo-60@3x.png",
|
||||
"filename" : "30243544-60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-20.png",
|
||||
"filename" : "30243544-20.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-20@2x.png",
|
||||
"filename" : "30243544-20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-29.png",
|
||||
"filename" : "30243544-29.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-29@2x.png",
|
||||
"filename" : "30243544-29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-40.png",
|
||||
"filename" : "30243544-40.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-40@2x.png",
|
||||
"filename" : "30243544-40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-76.png",
|
||||
"filename" : "30243544-76.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-76@2x.png",
|
||||
"filename" : "30243544-76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "mklogo-83.5@2x.png",
|
||||
"filename" : "30243544-83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "mklogo-1024.png",
|
||||
"filename" : "30243544-1024.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
|
||||
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "30243544.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13196" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13173"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13527"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -26,7 +26,7 @@
|
||||
<rect key="frame" x="32" y="98" width="310" height="171"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.99989169840000003" green="1" blue="0.99988096950000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="backgroundColor" red="0.24325716495513916" green="0.76319533586502075" blue="0.32751965522766113" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NHX-yN-7Jk" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" constant="16" id="ak3-MI-1bZ"/>
|
||||
<constraint firstItem="NHX-yN-7Jk" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="ebx-nK-jHq"/>
|
||||
|
||||
@@ -176,7 +176,7 @@ final class SampleData {
|
||||
|
||||
func getMessages(count: Int, completion: ([MockMessage]) -> Void) {
|
||||
var messages: [MockMessage] = []
|
||||
for _ in 0...count {
|
||||
for _ in 0..<count {
|
||||
messages.append(randomMessage())
|
||||
}
|
||||
completion(messages)
|
||||
|
||||
@@ -152,6 +152,26 @@ extension MessagesViewController: UICollectionViewDelegateFlowLayout {
|
||||
guard let messagesFlowLayout = collectionViewLayout as? MessagesCollectionViewFlowLayout else { return .zero }
|
||||
return messagesFlowLayout.sizeForItem(at: indexPath)
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
|
||||
guard let messagesCollectionView = collectionView as? MessagesCollectionView else { return .zero }
|
||||
guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return .zero }
|
||||
guard let messagesLayoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
|
||||
// Could pose a problem if subclass behaviors allows more than one item per section
|
||||
let indexPath = IndexPath(item: 0, section: section)
|
||||
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
||||
return messagesLayoutDelegate.headerViewSize(for: message, at: indexPath, in: messagesCollectionView)
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
|
||||
guard let messagesCollectionView = collectionView as? MessagesCollectionView else { return .zero }
|
||||
guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return .zero }
|
||||
guard let messagesLayoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
|
||||
// Could pose a problem if subclass behaviors allows more than one item per section
|
||||
let indexPath = IndexPath(item: 0, section: section)
|
||||
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
||||
return messagesLayoutDelegate.footerViewSize(for: message, at: indexPath, in: messagesCollectionView)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -230,27 +250,7 @@ extension MessagesViewController: UICollectionViewDataSource {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
|
||||
guard let messagesCollectionView = collectionView as? MessagesCollectionView else { return .zero }
|
||||
guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return .zero }
|
||||
guard let messagesLayoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
|
||||
// Could pose a problem if subclass behaviors allows more than one item per section
|
||||
let indexPath = IndexPath(item: 0, section: section)
|
||||
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
||||
return messagesLayoutDelegate.headerViewSize(for: message, at: indexPath, in: messagesCollectionView)
|
||||
}
|
||||
|
||||
open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
|
||||
guard let messagesCollectionView = collectionView as? MessagesCollectionView else { return .zero }
|
||||
guard let messagesDataSource = messagesCollectionView.messagesDataSource else { return .zero }
|
||||
guard let messagesLayoutDelegate = messagesCollectionView.messagesLayoutDelegate else { return .zero }
|
||||
// Could pose a problem if subclass behaviors allows more than one item per section
|
||||
let indexPath = IndexPath(item: 0, section: section)
|
||||
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
||||
return messagesLayoutDelegate.footerViewSize(for: message, at: indexPath, in: messagesCollectionView)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Keyboard Handling
|
||||
|
||||
@@ -183,8 +183,8 @@ open class InputBarButtonItem: UIButton {
|
||||
open func setSize(_ newValue: CGSize?, animated: Bool) {
|
||||
size = newValue
|
||||
if animated, let position = parentStackViewPosition {
|
||||
messageInputBar?.performLayout(animated) {
|
||||
self.messageInputBar?.layoutStackViews([position])
|
||||
messageInputBar?.performLayout(animated) { [weak self] in
|
||||
self?.messageInputBar?.layoutStackViews([position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ open class MessageLabel: UILabel, UIGestureRecognizerDelegate {
|
||||
return textStorage
|
||||
}()
|
||||
|
||||
private lazy var rangesForDetectors: [DetectorType: [(NSRange, Any?)]] = [:]
|
||||
private lazy var rangesForDetectors: [DetectorType: [(NSRange, MessageTextCheckingType)]] = [:]
|
||||
|
||||
// MARK: - Public Properties
|
||||
|
||||
@@ -321,13 +321,13 @@ open class MessageLabel: UILabel, UIGestureRecognizerDelegate {
|
||||
|
||||
private func detectorAttributes(for checkingResultType: NSTextCheckingResult.CheckingType) -> [NSAttributedStringKey: Any] {
|
||||
switch checkingResultType {
|
||||
case NSTextCheckingResult.CheckingType.address:
|
||||
case .address:
|
||||
return addressAttributes
|
||||
case NSTextCheckingResult.CheckingType.date:
|
||||
case .date:
|
||||
return dateAttributes
|
||||
case NSTextCheckingResult.CheckingType.phoneNumber:
|
||||
case .phoneNumber:
|
||||
return phoneNumberAttributes
|
||||
case NSTextCheckingResult.CheckingType.link:
|
||||
case .link:
|
||||
return urlAttributes
|
||||
default:
|
||||
fatalError("Received an unrecognized NSTextCheckingResult.CheckingType")
|
||||
@@ -349,24 +349,24 @@ open class MessageLabel: UILabel, UIGestureRecognizerDelegate {
|
||||
for result in checkingResults {
|
||||
|
||||
switch result.resultType {
|
||||
case NSTextCheckingResult.CheckingType.address:
|
||||
case .address:
|
||||
var ranges = rangesForDetectors[.address] ?? []
|
||||
let tuple = (result.range, result.addressComponents) as (NSRange, Any?)
|
||||
let tuple: (NSRange, MessageTextCheckingType) = (result.range, .addressComponents(result.addressComponents))
|
||||
ranges.append(tuple)
|
||||
rangesForDetectors.updateValue(ranges, forKey: .address)
|
||||
case NSTextCheckingResult.CheckingType.date:
|
||||
case .date:
|
||||
var ranges = rangesForDetectors[.date] ?? []
|
||||
let tuple = (result.range, result.date) as (NSRange, Any?)
|
||||
let tuple: (NSRange, MessageTextCheckingType) = (result.range, .date(result.date))
|
||||
ranges.append(tuple)
|
||||
rangesForDetectors.updateValue(ranges, forKey: .date)
|
||||
case NSTextCheckingResult.CheckingType.phoneNumber:
|
||||
case .phoneNumber:
|
||||
var ranges = rangesForDetectors[.phoneNumber] ?? []
|
||||
let tuple = (result.range, result.phoneNumber) as (NSRange, Any?)
|
||||
let tuple: (NSRange, MessageTextCheckingType) = (result.range, .phoneNumber(result.phoneNumber))
|
||||
ranges.append(tuple)
|
||||
rangesForDetectors.updateValue(ranges, forKey: .phoneNumber)
|
||||
case NSTextCheckingResult.CheckingType.link:
|
||||
case .link:
|
||||
var ranges = rangesForDetectors[.url] ?? []
|
||||
let tuple = (result.range, result.url) as (NSRange, Any?)
|
||||
let tuple: (NSRange, MessageTextCheckingType) = (result.range, .link(result.url))
|
||||
ranges.append(tuple)
|
||||
rangesForDetectors.updateValue(ranges, forKey: .url)
|
||||
default:
|
||||
@@ -428,20 +428,24 @@ open class MessageLabel: UILabel, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
private func handleGesture(for detectorType: DetectorType, value: Any?) {
|
||||
|
||||
switch detectorType {
|
||||
case .address:
|
||||
guard let addressComponents = value as? [String: String] else { return }
|
||||
handleAddress(addressComponents)
|
||||
case .phoneNumber:
|
||||
guard let phoneNumber = value as? String else { return }
|
||||
private func handleGesture(for detectorType: DetectorType, value: MessageTextCheckingType) {
|
||||
|
||||
switch value {
|
||||
case let .addressComponents(addressComponents):
|
||||
var transformedAddressComponents = [String: String]()
|
||||
guard let addressComponents = addressComponents else { return }
|
||||
addressComponents.forEach { (key, value) in
|
||||
transformedAddressComponents[key.rawValue] = value
|
||||
}
|
||||
handleAddress(transformedAddressComponents)
|
||||
case let .phoneNumber(phoneNumber):
|
||||
guard let phoneNumber = phoneNumber else { return }
|
||||
handlePhoneNumber(phoneNumber)
|
||||
case .date:
|
||||
guard let date = value as? Date else { return }
|
||||
case let .date(date):
|
||||
guard let date = date else { return }
|
||||
handleDate(date)
|
||||
case .url:
|
||||
guard let url = value as? URL else { return }
|
||||
case let .link(url):
|
||||
guard let url = url else { return }
|
||||
handleURL(url)
|
||||
}
|
||||
}
|
||||
@@ -463,3 +467,10 @@ open class MessageLabel: UILabel, UIGestureRecognizerDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate enum MessageTextCheckingType {
|
||||
case addressComponents([NSTextCheckingKey: String]?)
|
||||
case date(Date?)
|
||||
case phoneNumber(String?)
|
||||
case link(URL?)
|
||||
}
|
||||
|
||||