├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Demo ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Demo.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Demo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── .DS_Store │ │ ├── Alarm.imageset │ │ │ ├── Alarm.png │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Camera.imageset │ │ │ ├── Camera.png │ │ │ └── Contents.json │ │ ├── Contacts.imageset │ │ │ ├── Contacts.png │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── FT_Add.imageset │ │ │ ├── Contents.json │ │ │ └── FT_Add@2x.png │ │ ├── FT_Error.imageset │ │ │ ├── Contents.json │ │ │ └── FT_Error@2x.png │ │ ├── FT_Record.imageset │ │ │ ├── Contents.json │ │ │ └── FT_Record@2x.png │ │ ├── Mail.imageset │ │ │ ├── Contents.json │ │ │ └── Mail.png │ │ ├── Media_Pause.imageset │ │ │ ├── Contents.json │ │ │ └── Media_Pause.png │ │ ├── Media_Play.imageset │ │ │ ├── Contents.json │ │ │ └── Media_Play.png │ │ ├── Messages.imageset │ │ │ ├── Contents.json │ │ │ └── Messages.png │ │ ├── Music.imageset │ │ │ ├── Contents.json │ │ │ └── Music.png │ │ ├── Phone.imageset │ │ │ ├── Contents.json │ │ │ └── Phone.png │ │ ├── Photos.imageset │ │ │ ├── Contents.json │ │ │ └── Photos.png │ │ ├── Settings.imageset │ │ │ ├── Contents.json │ │ │ └── Settings.png │ │ ├── VideoChat.imageset │ │ │ ├── Contents.json │ │ │ └── Videocamera.png │ │ ├── Videos.imageset │ │ │ ├── Contents.json │ │ │ └── Videos.png │ │ ├── Voice_Record.imageset │ │ │ ├── Contents.json │ │ │ └── Voice_Record.png │ │ └── Weather.imageset │ │ │ ├── Contents.json │ │ │ └── Weather.png │ ├── Base.lproj │ │ ├── .DS_Store │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── ChatRoomTableViewCell.swift │ ├── ChatRoomViewController.swift │ ├── ChatTableViewController.swift │ ├── ContactsTableViewCell.swift │ ├── ContactsViewController.swift │ ├── Demo-Bridging-Header.h │ ├── Info.plist │ ├── SignInViewController.swift │ ├── dog.jpg │ └── pinyin.bin ├── Podfile ├── Podfile.lock └── Pods │ ├── FTImageSize │ ├── FTImageSize │ │ └── FTImageSize.swift │ ├── LICENSE │ └── README.md │ ├── FTIndicator │ ├── FTIndicator │ │ ├── FTIndicator.h │ │ ├── FTIndicator.m │ │ ├── FTNotificationIndicator │ │ │ ├── FTNotificationIndicator.h │ │ │ └── FTNotificationIndicator.m │ │ ├── FTProgressIndicator │ │ │ ├── FTProgressIndicator.bundle │ │ │ │ ├── ft_failure.png │ │ │ │ ├── ft_failure_dark.png │ │ │ │ ├── ft_info.png │ │ │ │ ├── ft_info_dark.png │ │ │ │ ├── ft_success.png │ │ │ │ └── ft_success_dark.png │ │ │ ├── FTProgressIndicator.h │ │ │ └── FTProgressIndicator.m │ │ └── FTToastIndicator │ │ │ ├── FTToastIndicator.h │ │ │ └── FTToastIndicator.m │ ├── LICENSE │ └── README.md │ ├── FTPickerView │ ├── FTPickerView │ │ ├── FTPickerView.h │ │ └── FTPickerView.m │ ├── LICENSE │ └── README.md │ ├── Kingfisher │ ├── LICENSE │ ├── README.md │ └── Sources │ │ ├── AnimatedImageView.swift │ │ ├── Box.swift │ │ ├── CacheSerializer.swift │ │ ├── Filter.swift │ │ ├── FormatIndicatedCacheSerializer.swift │ │ ├── Image.swift │ │ ├── ImageCache.swift │ │ ├── ImageDownloader.swift │ │ ├── ImageModifier.swift │ │ ├── ImagePrefetcher.swift │ │ ├── ImageProcessor.swift │ │ ├── ImageTransition.swift │ │ ├── ImageView+Kingfisher.swift │ │ ├── Indicator.swift │ │ ├── Kingfisher.h │ │ ├── Kingfisher.swift │ │ ├── KingfisherManager.swift │ │ ├── KingfisherOptionsInfo.swift │ │ ├── Placeholder.swift │ │ ├── RequestModifier.swift │ │ ├── Resource.swift │ │ ├── String+MD5.swift │ │ ├── ThreadHelper.swift │ │ └── UIButton+Kingfisher.swift │ ├── Manifest.lock │ ├── Pods.xcodeproj │ └── project.pbxproj │ └── Target Support Files │ ├── FTImageSize │ ├── FTImageSize-dummy.m │ ├── FTImageSize-prefix.pch │ ├── FTImageSize-umbrella.h │ ├── FTImageSize.modulemap │ ├── FTImageSize.xcconfig │ └── Info.plist │ ├── FTIndicator │ ├── FTIndicator-dummy.m │ ├── FTIndicator-prefix.pch │ ├── FTIndicator-umbrella.h │ ├── FTIndicator.modulemap │ ├── FTIndicator.xcconfig │ └── Info.plist │ ├── FTPickerView │ ├── FTPickerView-dummy.m │ ├── FTPickerView-prefix.pch │ ├── FTPickerView-umbrella.h │ ├── FTPickerView.modulemap │ ├── FTPickerView.xcconfig │ └── Info.plist │ ├── Kingfisher │ ├── Info.plist │ ├── Kingfisher-dummy.m │ ├── Kingfisher-prefix.pch │ ├── Kingfisher-umbrella.h │ ├── Kingfisher.modulemap │ └── Kingfisher.xcconfig │ └── Pods-Demo │ ├── Info.plist │ ├── Pods-Demo-acknowledgements.markdown │ ├── Pods-Demo-acknowledgements.plist │ ├── Pods-Demo-dummy.m │ ├── Pods-Demo-frameworks.sh │ ├── Pods-Demo-resources.sh │ ├── Pods-Demo-umbrella.h │ ├── Pods-Demo.debug.xcconfig │ ├── Pods-Demo.modulemap │ └── Pods-Demo.release.xcconfig ├── FTChatMessage.mdj ├── FTChatMessage ├── FTChatMessageCell │ ├── .DS_Store │ ├── FTChatMessageBubbleItem │ │ ├── FTChatMessageBubbleAudioItem.swift │ │ ├── FTChatMessageBubbleImageItem.swift │ │ ├── FTChatMessageBubbleItem.swift │ │ ├── FTChatMessageBubbleLocationItem.swift │ │ ├── FTChatMessageBubbleTextItem.swift │ │ └── FTChatMessageBubbleVideoItem.swift │ ├── FTChatMessageCell.swift │ └── FTChatMessageDeliverStatusView.swift ├── FTChatMessageConversation.swift ├── FTChatMessageDataSource.swift ├── FTChatMessageDelegate.swift ├── FTChatMessageExtensions.swift ├── FTChatMessageHeader │ └── FTChatMessageHeader.swift ├── FTChatMessageInput │ ├── FTChatMessageAccessoryItem.swift │ ├── FTChatMessageAccessoryItem.xib │ ├── FTChatMessageAccessoryView.swift │ ├── FTChatMessageAccessoryView.xib │ ├── FTChatMessageInputView.swift │ ├── FTChatMessageInputView.xib │ ├── FTChatMessageRecorderView.swift │ └── FTChatMessageRecorderView.xib ├── FTChatMessageMarcos.swift ├── FTChatMessageModel.swift ├── FTChatMessageTableViewController+DataSource.swift ├── FTChatMessageTableViewController.swift └── FTChatMessageUserModel.swift ├── LICENSE ├── README.md └── ResourceImages ├── 1.jpg ├── 2.jpg ├── 3.jpg └── ChatMessageDemo.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | .DS_Store 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata/ 20 | 21 | ## Other 22 | *.moved-aside 23 | *.xcuserstate 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | # Pods/ 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language : swift 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # FTChatMessage CHANGELOG 2 | 3 | 4 | |Date|CHANGELOG| 5 | |:------:|:------| 6 | | 2016.04.06 | now supports images | 7 | | 2016.04.08 | add time and sender name support | 8 | | 2016.04.09 | change input bar into UIToolBar instead of UIView | 9 | | 2016.04.12 | it's getting better | 10 | | 2016.04.19 | I have been busy. | 11 | | 2016.04.20 | add record view. functions and animations not ready. | 12 | | 2016.04.26 | switch input view done ! | 13 | | 2016.04.27 | add deliver status and finish accessory view ! it's 4.28 now... | 14 | | 2016.05.02 | Audio! | 15 | | 2016.05.08 | fix something, test something | 16 | | 2016.08.08 | not doing anything. if anyone want use it, I will try finish it. | 17 | | 2016.08.21 | rewriting some module | 18 | | 2016.08.22 | rewriting some module, make some of the view with xib | 19 | | 2016.08.23 | Move repo | 20 | | 2016.09.08 | simplify some methods | 21 | | 2016.10.05 | add some methods and I really wanna rewrite this. I will maybe do this in the near future. | 22 | | 2018.05.14 | update | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Demo/Demo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2016/9/22. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | 19 | 20 | return true 21 | } 22 | 23 | func applicationWillResignActive(_ application: UIApplication) { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 26 | } 27 | 28 | func applicationDidEnterBackground(_ application: UIApplication) { 29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | func applicationWillEnterForeground(_ application: UIApplication) { 34 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 35 | } 36 | 37 | func applicationDidBecomeActive(_ application: UIApplication) { 38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 39 | } 40 | 41 | func applicationWillTerminate(_ application: UIApplication) { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Alarm.imageset/Alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Alarm.imageset/Alarm.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Alarm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Alarm.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Camera.imageset/Camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Camera.imageset/Camera.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Camera.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contacts.imageset/Contacts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Contacts.imageset/Contacts.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contacts.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Contacts.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Add.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "FT_Add@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Add.imageset/FT_Add@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/FT_Add.imageset/FT_Add@2x.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Error.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "FT_Error@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Error.imageset/FT_Error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/FT_Error.imageset/FT_Error@2x.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "FT_Record@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/FT_Record.imageset/FT_Record@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/FT_Record.imageset/FT_Record@2x.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Mail.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Mail.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Mail.imageset/Mail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Mail.imageset/Mail.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Media_Pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Media_Pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Media_Pause.imageset/Media_Pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Media_Pause.imageset/Media_Pause.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Media_Play.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Media_Play.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Media_Play.imageset/Media_Play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Media_Play.imageset/Media_Play.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Messages.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Messages.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Messages.imageset/Messages.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Messages.imageset/Messages.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Music.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Music.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Music.imageset/Music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Music.imageset/Music.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Phone.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Phone.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Phone.imageset/Phone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Phone.imageset/Phone.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Photos.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Photos.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Photos.imageset/Photos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Photos.imageset/Photos.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Settings.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Settings.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Settings.imageset/Settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Settings.imageset/Settings.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/VideoChat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Videocamera.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/VideoChat.imageset/Videocamera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/VideoChat.imageset/Videocamera.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Videos.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Videos.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Videos.imageset/Videos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Videos.imageset/Videos.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Voice_Record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Voice_Record.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Voice_Record.imageset/Voice_Record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Voice_Record.imageset/Voice_Record.png -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Weather.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Weather.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Weather.imageset/Weather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Assets.xcassets/Weather.imageset/Weather.png -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/Base.lproj/.DS_Store -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Demo/Demo/ChatRoomTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTableViewCell.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/3/27. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ChatRoomTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var iconImageView: UIImageView! 14 | @IBOutlet weak var nameLabel: UILabel! 15 | @IBOutlet weak var contentLabel: UILabel! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | 20 | } 21 | 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /Demo/Demo/ChatRoomViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/15. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FTIndicator 11 | 12 | 13 | class ChatRoomViewController: UIViewController, UITableViewDelegate,UITableViewDataSource{ 14 | 15 | @IBOutlet weak var chatListTableView: UITableView! 16 | var messageArray : [FTChatMessageModel] = [] 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | self.navigationItem.hidesBackButton = true 21 | 22 | self.chatListTableView.addSubview(refreshControl); 23 | 24 | } 25 | override func viewDidAppear(_ animated: Bool) { 26 | super.viewDidAppear(animated) 27 | 28 | self.reloadConversations() 29 | } 30 | 31 | func reloadConversations() { 32 | self.chatListTableView.reloadData() 33 | } 34 | 35 | 36 | 37 | lazy var refreshControl : UIRefreshControl = { 38 | let refresh : UIRefreshControl = UIRefreshControl.init(frame: CGRect.init(x: 0, y: 0, width: self.view.frame.width, height: 60)) 39 | refresh.addTarget(self, action: #selector(self.onPullToRefreshTriggered), for: UIControlEvents.valueChanged) 40 | return refresh; 41 | }() 42 | 43 | @IBAction func onPullToRefreshTriggered() { 44 | self.reloadConversations() 45 | DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + Double(Int64(2 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { 46 | self.refreshControl.endRefreshing() 47 | } 48 | } 49 | 50 | 51 | @IBAction func addItemAction(_ sender: UIBarButtonItem) { 52 | self.performSegue(withIdentifier: "ChooseContactsToChat", sender: self) 53 | } 54 | 55 | /* UITableViewDelegate,UITableViewDataSource */ 56 | 57 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 58 | return 0.01 59 | } 60 | func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 61 | return 0.01 62 | } 63 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 64 | return 1 65 | } 66 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 67 | let cell : ChatRoomTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ChatRoomTableViewCellIndentifier") as! ChatRoomTableViewCell 68 | return cell 69 | } 70 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 71 | tableView.deselectRow(at: indexPath, animated: true) 72 | 73 | self.didTappedCell(at: indexPath) 74 | } 75 | 76 | func didTappedCell(at indexPath: IndexPath) { 77 | 78 | let chat : ChatTableViewController = self.storyboard?.instantiateViewController(withIdentifier: "ChatTableViewController") as! ChatTableViewController 79 | 80 | self.navigationController?.pushViewController(chat, animated: true) 81 | 82 | } 83 | 84 | 85 | } 86 | 87 | -------------------------------------------------------------------------------- /Demo/Demo/ChatTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatTableViewController.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FTIndicator 11 | 12 | class ChatTableViewController: FTChatMessageTableViewController,FTChatMessageAccessoryViewDelegate,FTChatMessageAccessoryViewDataSource,FTChatMessageRecorderViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate{ 13 | 14 | let sender1 = FTChatMessageUserModel.init(id: "1", name: "Someone", icon_url: "http://ww3.sinaimg.cn/mw600/6cca1403jw1f3lrknzxczj20gj0g0t96.jpg", extra_data: nil, isSelf: false) 15 | 16 | 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.navigationItem.setRightBarButton(UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.add, target: self, action: #selector(self.addNewIncomingMessage)), animated: true) 22 | 23 | messageRecordView.recorderDelegate = self 24 | messageAccessoryView.setupWithDataSource(self , accessoryViewDelegate : self) 25 | 26 | chatMessageDataArray = self.loadDefaultMessages() 27 | 28 | } 29 | 30 | //MARK: - addNewIncomingMessage 31 | 32 | @objc func addNewIncomingMessage() { 33 | 34 | let message8 = FTChatMessageModel(data: "New Message added, try something else.", time: "4.12 22:42", from: sender1, type: .text) 35 | self.addNewMessage(message8) 36 | 37 | } 38 | 39 | func loadDefaultMessages() -> [FTChatMessageModel] { 40 | 41 | let message1 = FTChatMessageModel(data: "最近有点无聊,抽点时间写了这个聊天的UI框架。", time: "4.12 21:09:50", from: sender1, type: .text) 42 | let message2 = FTChatMessageModel(data: "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈", time: "4.12 21:09:51", from: sender1, type: .video) 43 | let message3 = FTChatMessageImageModel(data: "http://ww2.sinaimg.cn/mw600/6aa09e8fgw1f8iquoznw2j20dw0bv0uk.jpg", time: "4.12 21:09:52", from: sender1, type: .image) 44 | message3.imageUrl = "http://ww2.sinaimg.cn/mw600/6aa09e8fgw1f8iquoznw2j20dw0bv0uk.jpg" 45 | 46 | 47 | let message4 = FTChatMessageModel(data: "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈", time: "4.12 21:09:53", from: sender2, type: .text) 48 | let message5 = FTChatMessageModel(data: "文字背景不是图片,是用贝塞尔曲线画的,效率应该不高,后期优化", time: "4.12 21:09:53", from: sender2, type: .text) 49 | let message6 = FTChatMessageModel(data: "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈", time: "4.12 21:09:54", from: sender2, type: .text) 50 | let message8 = FTChatMessageImageModel(data: "http://wx3.sinaimg.cn/mw600/9e745efdly1fbmfs45minj20tg0xcq6v.jpg", time: "4.12 21:09:56", from: sender1, type: .image) 51 | message8.imageUrl = "http://wx3.sinaimg.cn/mw600/9e745efdly1fbmfs45minj20tg0xcq6v.jpg" 52 | 53 | 54 | let message7 = FTChatMessageModel(data: "哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈", time: "4.12 21:09:55", from: sender1, type: .text) 55 | 56 | 57 | let array = [message1,message2,message3,message4,message5,message6,message8,message7] 58 | 59 | return array; 60 | 61 | } 62 | 63 | 64 | func getAccessoryItemTitleArray() -> [String] { 65 | return ["Alarm","Camera","Contacts","Mail","Messages","Music","Phone","Photos","Settings","VideoChat","Videos","Weather"] 66 | } 67 | 68 | 69 | //MARK: - FTChatMessageAccessoryViewDataSource 70 | 71 | func ftChatMessageAccessoryViewModelArray() -> [FTChatMessageAccessoryViewModel] { 72 | var array : [FTChatMessageAccessoryViewModel] = [] 73 | let titleArray = self.getAccessoryItemTitleArray() 74 | for i in 0...titleArray.count-1 { 75 | let string = titleArray[i] 76 | array.append(FTChatMessageAccessoryViewModel.init(title: string, iconImage: UIImage(named: string)!)) 77 | } 78 | return array 79 | } 80 | 81 | //MARK: - FTChatMessageAccessoryViewDelegate 82 | 83 | func ftChatMessageAccessoryViewDidTappedOnItemAtIndex(_ index: NSInteger) { 84 | 85 | if index == 0 { 86 | 87 | let imagePicker : UIImagePickerController = UIImagePickerController() 88 | imagePicker.sourceType = .photoLibrary 89 | imagePicker.delegate = self 90 | self.present(imagePicker, animated: true, completion: { 91 | 92 | }) 93 | }else{ 94 | let string = "I just tapped at accessory view at index : \(index)" 95 | 96 | print(string) 97 | 98 | // FTIndicator.showInfo(withMessage: string) 99 | 100 | let message2 = FTChatMessageModel(data: string, time: "4.12 21:09:51", from: sender2, type: .text) 101 | 102 | self.addNewMessage(message2) 103 | } 104 | } 105 | 106 | //MARK: - FTChatMessageRecorderViewDelegate 107 | 108 | func ft_chatMessageRecordViewDidStartRecording(){ 109 | print("Start recording...") 110 | FTIndicator.showProgress(withMessage: "Recording...") 111 | } 112 | func ft_chatMessageRecordViewDidCancelRecording(){ 113 | print("Recording canceled.") 114 | FTIndicator.dismissProgress() 115 | } 116 | func ft_chatMessageRecordViewDidStopRecording(_ duriation: TimeInterval, file: Data?){ 117 | print("Recording ended!") 118 | FTIndicator.showSuccess(withMessage: "Record done.") 119 | 120 | let message2 = FTChatMessageModel(data: "", time: "4.12 21:09:51", from: sender2, type: .audio) 121 | 122 | self.addNewMessage(message2) 123 | 124 | } 125 | 126 | 127 | func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) { 128 | 129 | picker.dismiss(animated: true) { 130 | 131 | let image : UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage 132 | let message2 = FTChatMessageImageModel(data: "", time: "4.12 21:09:51", from: self.sender2, type: .image) 133 | message2.image = image; 134 | self.addNewMessage(message2) 135 | } 136 | } 137 | 138 | func saveImageToDisk(image: UIImage) -> String { 139 | 140 | 141 | return "" 142 | } 143 | 144 | 145 | } 146 | -------------------------------------------------------------------------------- /Demo/Demo/ContactsTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactsTableViewCell.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2016/10/20. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContactsTableViewCell: UITableViewCell { 12 | 13 | @IBOutlet weak var iconImageView: UIImageView! 14 | @IBOutlet weak var nameLabel: UILabel! 15 | 16 | // open func setupWithUser(user : NIMUser) { 17 | // if let iconUrl : String = user.userInfo?.avatarUrl { 18 | // self.iconImageView.kf.setImage(with: URL(string: iconUrl)!) 19 | // } 20 | // self.nameLabel.text = user.userInfo?.nickName 21 | // } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Demo/Demo/ContactsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactsViewController.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2016/10/20. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ContactsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{ 12 | 13 | @IBOutlet weak var tableView: UITableView! 14 | @IBOutlet weak var searchBar: UISearchBar! 15 | 16 | 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | 22 | 23 | } 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | /* UITableViewDelegate,UITableViewDataSource */ 32 | 33 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 34 | return 2 35 | } 36 | func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 37 | return 0.01 38 | } 39 | func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 40 | return 0.01 41 | } 42 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 43 | return 70 44 | } 45 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 46 | let cell : ContactsTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ContactsTableViewCellIdentifier") as! ContactsTableViewCell 47 | return cell 48 | } 49 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 50 | tableView.deselectRow(at: indexPath, animated: true) 51 | 52 | self.didTappedCell(at: indexPath) 53 | } 54 | 55 | func didTappedCell(at indexPath: IndexPath) { 56 | 57 | let chat : ChatTableViewController = self.storyboard?.instantiateViewController(withIdentifier: "ChatTableViewController") as! ChatTableViewController 58 | 59 | self.navigationController?.pushViewController(chat, animated: true) 60 | 61 | } 62 | 63 | 64 | } 65 | -------------------------------------------------------------------------------- /Demo/Demo/Demo-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /Demo/Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | FTChaMessage 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1.0.1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSPhotoLibraryUsageDescription 31 | $(PRODUCT_BUNDLE_IDENTIFIER) would like to use your photo library. 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | 44 | UISupportedInterfaceOrientations~ipad 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationPortraitUpsideDown 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Demo/Demo/SignInViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignInViewController.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2016/10/18. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FTIndicator 11 | import FTPickerView 12 | 13 | class SignInViewController: UIViewController { 14 | 15 | @IBOutlet weak var accountTextField: UITextField! 16 | @IBOutlet weak var passwordTextField: UITextField! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | self.accountTextField.text = "liufengting" 22 | self.passwordTextField.text = "123456" 23 | 24 | 25 | } 26 | 27 | var accounts : [String] = ["liufengting", 28 | "123456", 29 | "Test01", 30 | "Test02", 31 | "Test03", 32 | "Test04", 33 | "Test05"] 34 | 35 | 36 | @IBAction func chooseAccountAction(_ sender: UIButton) { 37 | FTPickerView.show(withTitle: "Choose account", 38 | nameArray: self.accounts, 39 | doneBlock: { (selectedIndex) in 40 | self.accountTextField.text = self.accounts[selectedIndex] 41 | }) { 42 | 43 | } 44 | } 45 | 46 | @IBAction func signInAction(_ sender: UIButton) { 47 | 48 | 49 | // if self.accountTextField.text == "" { 50 | // return; 51 | // } 52 | // if self.passwordTextField.text == "" { 53 | // return; 54 | // } 55 | // self.view.endEditing(true) 56 | // 57 | // FTIndicator.showProgressWithmessage("Signing in...", userInteractionEnable:false) 58 | // 59 | // NIMSDK.shared().loginManager.login(self.accountTextField.text!, token: (self.passwordTextField.text! as NSString).tokenByPassword()) { (error) in 60 | // if (error != nil) { 61 | // FTIndicator.showError(withMessage: "Sign in failed with code:\((error! as NSError).code)") 62 | // }else{ 63 | // FTIndicator.showSuccess(withMessage: "Sign in succeeded."); 64 | // 65 | // DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + Double(Int64(1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { 66 | self.performSegue(withIdentifier: "SigninToHome", sender: self) 67 | // } 68 | // } 69 | // } 70 | } 71 | 72 | override func touchesBegan(_ touches: Set, with event: UIEvent?) { 73 | self.view.endEditing(true) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Demo/Demo/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/dog.jpg -------------------------------------------------------------------------------- /Demo/Demo/pinyin.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Demo/pinyin.bin -------------------------------------------------------------------------------- /Demo/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'Demo' do 4 | 5 | pod 'Kingfisher' 6 | pod 'FTIndicator' 7 | pod 'FTPickerView' 8 | pod 'FTImageSize' 9 | 10 | end 11 | -------------------------------------------------------------------------------- /Demo/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FTImageSize (0.0.4) 3 | - FTIndicator (1.2.9): 4 | - FTIndicator/FTNotificationIndicator (= 1.2.9) 5 | - FTIndicator/FTProgressIndicator (= 1.2.9) 6 | - FTIndicator/FTToastIndicator (= 1.2.9) 7 | - FTIndicator/FTNotificationIndicator (1.2.9) 8 | - FTIndicator/FTProgressIndicator (1.2.9) 9 | - FTIndicator/FTToastIndicator (1.2.9) 10 | - FTPickerView (1.0.2) 11 | - Kingfisher (4.7.0) 12 | 13 | DEPENDENCIES: 14 | - FTImageSize 15 | - FTIndicator 16 | - FTPickerView 17 | - Kingfisher 18 | 19 | SPEC CHECKSUMS: 20 | FTImageSize: 29c6fc0830dbe33973286148a8e32cd582a9f26d 21 | FTIndicator: f7f071fd159e5befa1d040a9ef2e3ab53fa9322c 22 | FTPickerView: 3fb6055e057ac608435ce7d938f2bbc0640f4948 23 | Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6 24 | 25 | PODFILE CHECKSUM: 0897bbd78b2258d0c7f01ee2e9d5eb5a802cde17 26 | 27 | COCOAPODS: 1.4.0 28 | -------------------------------------------------------------------------------- /Demo/Pods/FTImageSize/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Liu Fengting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demo/Pods/FTImageSize/README.md: -------------------------------------------------------------------------------- 1 | # FTImageSize 2 | FTImageSize 3 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTIndicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // FTIndicator.m 3 | // FTIndicator 4 | // 5 | // Created by liufengting on 16/7/21. 6 | // Copyright © 2016年 liufengting ( https://github.com/liufengting ). All rights reserved. 7 | // 8 | 9 | #import "FTIndicator.h" 10 | 11 | @interface FTIndicator () 12 | 13 | @end 14 | 15 | @implementation FTIndicator 16 | 17 | + (void)setIndicatorStyleToDefaultStyle 18 | { 19 | [self setIndicatorStyle:UIBlurEffectStyleLight]; 20 | } 21 | 22 | + (void)setIndicatorStyle:(UIBlurEffectStyle)style 23 | { 24 | [FTToastIndicator setToastIndicatorStyle:style]; 25 | [FTProgressIndicator setProgressIndicatorStyle:style]; 26 | [FTNotificationIndicator setNotificationIndicatorStyle:style]; 27 | } 28 | 29 | #pragma mark - FTToastIndicator 30 | /** 31 | * FTToastIndicator 32 | */ 33 | + (void)showToastMessage:(NSString *)toastMessage 34 | { 35 | [FTToastIndicator showToastMessage:toastMessage]; 36 | } 37 | 38 | + (void)dismissToast 39 | { 40 | [FTToastIndicator dismiss]; 41 | } 42 | 43 | 44 | #pragma mark - FTProgressIndicator 45 | /** 46 | * FTProgressIndicator 47 | */ 48 | + (void)showProgressWithMessage:(NSString *)message 49 | { 50 | [self showProgressWithMessage:message userInteractionEnable:YES]; 51 | } 52 | + (void)showProgressWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 53 | { 54 | [FTProgressIndicator showProgressWithMessage:message userInteractionEnable:userInteractionEnable]; 55 | } 56 | 57 | 58 | + (void)showInfoWithMessage:(NSString *)message 59 | { 60 | [self showInfoWithMessage:message image:nil userInteractionEnable:YES]; 61 | } 62 | + (void)showInfoWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 63 | { 64 | [self showInfoWithMessage:message image:nil userInteractionEnable:userInteractionEnable]; 65 | } 66 | + (void)showInfoWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable 67 | { 68 | [FTProgressIndicator showInfoWithMessage:message image:image userInteractionEnable:userInteractionEnable]; 69 | } 70 | 71 | 72 | + (void)showSuccessWithMessage:(NSString *)message 73 | { 74 | [self showSuccessWithMessage:message image:nil userInteractionEnable:YES]; 75 | } 76 | + (void)showSuccessWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 77 | { 78 | [self showSuccessWithMessage:message image:nil userInteractionEnable:userInteractionEnable]; 79 | } 80 | + (void)showSuccessWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable 81 | { 82 | [FTProgressIndicator showSuccessWithMessage:message image:image userInteractionEnable:userInteractionEnable]; 83 | } 84 | 85 | 86 | + (void)showErrorWithMessage:(NSString *)message 87 | { 88 | [self showErrorWithMessage:message image:nil userInteractionEnable:YES]; 89 | } 90 | + (void)showErrorWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 91 | { 92 | [self showErrorWithMessage:message image:nil userInteractionEnable:userInteractionEnable]; 93 | } 94 | + (void)showErrorWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable 95 | { 96 | [FTProgressIndicator showErrorWithMessage:message image:image userInteractionEnable:userInteractionEnable]; 97 | } 98 | 99 | 100 | + (void)dismissProgress 101 | { 102 | [FTProgressIndicator dismiss]; 103 | } 104 | 105 | #pragma mark - FTNotificationIndicator 106 | /** 107 | * FTNotificationIndicator 108 | */ 109 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message 110 | { 111 | [self showNotificationWithImage:nil title:title message:message autoDismiss:YES tapHandler:nil completion:nil]; 112 | } 113 | 114 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler 115 | { 116 | [self showNotificationWithImage:nil title:title message:message autoDismiss:YES tapHandler:tapHandler completion:nil]; 117 | } 118 | 119 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion 120 | { 121 | [self showNotificationWithImage:nil title:title message:message autoDismiss:YES tapHandler:tapHandler completion:completion]; 122 | } 123 | 124 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message 125 | { 126 | [self showNotificationWithImage:image title:title message:message autoDismiss:YES tapHandler:nil completion:nil]; 127 | } 128 | 129 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler 130 | { 131 | [self showNotificationWithImage:image title:title message:message autoDismiss:YES tapHandler:tapHandler completion:nil]; 132 | } 133 | 134 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion 135 | { 136 | [self showNotificationWithImage:image title:title message:message autoDismiss:YES tapHandler:tapHandler completion:completion]; 137 | } 138 | 139 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message autoDismiss:(BOOL)autoDismiss tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion 140 | { 141 | [FTNotificationIndicator showNotificationWithImage:image title:title message:message autoDismiss:autoDismiss tapHandler:tapHandler completion:completion]; 142 | } 143 | 144 | + (void)dismissNotification 145 | { 146 | [FTNotificationIndicator dismiss]; 147 | } 148 | 149 | @end 150 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTNotificationIndicator/FTNotificationIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTNotificationIndicator.h 3 | // FTIndicator 4 | // 5 | // Created by liufengting on 16/7/26. 6 | // Copyright © 2016年 liufengting ( https://github.com/liufengting ). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #pragma mark - FTNotificationIndicator 12 | 13 | typedef void (^FTNotificationTapHandler)(void); 14 | typedef void (^FTNotificationCompletion)(void); 15 | 16 | /** 17 | FTNotificationIndicator 18 | */ 19 | @interface FTNotificationIndicator : NSObject 20 | 21 | /** 22 | setIndicatorStyleToDefaultStyle 23 | */ 24 | + (void)setNotificationIndicatorStyleToDefaultStyle; 25 | /** 26 | setIndicatorStyle 27 | 28 | @param style style 29 | */ 30 | + (void)setNotificationIndicatorStyle:(UIBlurEffectStyle)style; 31 | /** 32 | showNotificationWithTitle message 33 | 34 | @param title title 35 | @param message message 36 | */ 37 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message; 38 | /** 39 | showNotificationWithTitle message tapHandler 40 | 41 | @param title title 42 | @param message message 43 | @param tapHandler tapHandler 44 | */ 45 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 46 | /** 47 | showNotificationWithTitle message tapHandler completion 48 | 49 | @param title title 50 | @param message message 51 | @param tapHandler tapHandler 52 | @param completion completion 53 | */ 54 | + (void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 55 | /** 56 | showNotificationWithImage title message 57 | 58 | @param image image 59 | @param title title 60 | @param message message 61 | */ 62 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message; 63 | /** 64 | showNotificationWithImage title message tapHandler 65 | 66 | @param image image 67 | @param title title 68 | @param message message 69 | @param tapHandler tapHandler 70 | */ 71 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 72 | /** 73 | showNotificationWithImage title message tapHandler completion 74 | 75 | 76 | @param image image 77 | @param title title 78 | @param message message 79 | @param tapHandler tapHandler 80 | @param completion completion 81 | */ 82 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 83 | 84 | 85 | /** 86 | showNotificationWithImage title message autoDismiss tapHandler completion 87 | 88 | !!!!!!!!! Only this method suports not dismiss automatically, user has to tap or swipe to dismiss. 89 | 90 | @param image image 91 | @param title title 92 | @param message message 93 | @param autoDismiss autoDismiss 94 | @param tapHandler tapHandler 95 | @param completion completion 96 | */ 97 | + (void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message autoDismiss:(BOOL)autoDismiss tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 98 | /** 99 | dismiss 100 | */ 101 | + (void)dismiss; 102 | 103 | 104 | /** 105 | Default delay until dismiss notification 106 | 107 | @param time Delay in seconds 108 | */ 109 | + (void)setDefaultDismissTime:(NSTimeInterval)time; 110 | 111 | 112 | /** 113 | Default delay until dismiss notification 114 | 115 | @return Delay in seconds 116 | */ 117 | + (NSTimeInterval)defaultDismissTime; 118 | 119 | @end 120 | 121 | #pragma mark - FTNotificationIndicatorView 122 | 123 | /** 124 | FTNotificationIndicatorView 125 | */ 126 | @interface FTNotificationIndicatorView : UIVisualEffectView 127 | /** 128 | showWithImage 129 | 130 | @param image image 131 | @param title title 132 | @param message message 133 | @param style style 134 | */ 135 | - (void)showWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message style:(UIBlurEffectStyle)style; 136 | /** 137 | getFrameForNotificationViewWithImage 138 | 139 | @param image image 140 | @param notificationMessage message 141 | @return CGSize 142 | */ 143 | - (CGSize )getFrameForNotificationViewWithImage:(UIImage *)image message:(NSString *)notificationMessage; 144 | 145 | @end 146 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_failure.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_failure_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_failure_dark.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_info.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_info_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_info_dark.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_success.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_success_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_success_dark.png -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTProgressIndicator.h 3 | // FTIndicator 4 | // 5 | // Created by liufengting on 16/7/26. 6 | // Copyright © 2016年 liufengting ( https://github.com/liufengting ). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #pragma mark - FTProgressIndicatorMessageType 12 | 13 | /** 14 | FTProgressIndicatorMessageType 15 | 16 | - FTProgressIndicatorMessageTypeInfo: FTProgressIndicatorMessageTypeInfo 17 | - FTProgressIndicatorMessageTypeSuccess: FTProgressIndicatorMessageTypeSuccess 18 | - FTProgressIndicatorMessageTypeError: FTProgressIndicatorMessageTypeError 19 | - FTProgressIndicatorMessageTypeProgress: FTProgressIndicatorMessageTypeProgress 20 | */ 21 | typedef NS_ENUM(NSUInteger, FTProgressIndicatorMessageType) { 22 | FTProgressIndicatorMessageTypeInfo, 23 | FTProgressIndicatorMessageTypeSuccess, 24 | FTProgressIndicatorMessageTypeError, 25 | FTProgressIndicatorMessageTypeProgress 26 | }; 27 | 28 | #pragma mark - FTProgressIndicator 29 | /** 30 | FTProgressIndicator 31 | */ 32 | @interface FTProgressIndicator : NSObject 33 | 34 | /** 35 | showProgressWithMessage 36 | 37 | @param message message 38 | */ 39 | + (void)showProgressWithMessage:(NSString *)message; 40 | 41 | /** 42 | showProgressWithMessage userInteractionEnable 43 | 44 | @param message message 45 | @param userInteractionEnable userInteractionEnable 46 | */ 47 | + (void)showProgressWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 48 | 49 | /** 50 | showInfoWithMessage 51 | 52 | @param message message 53 | */ 54 | + (void)showInfoWithMessage:(NSString *)message; 55 | 56 | /** 57 | showInfoWithMessage userInteractionEnable 58 | 59 | @param message message 60 | @param userInteractionEnable userInteractionEnable 61 | */ 62 | + (void)showInfoWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 63 | 64 | /** 65 | showInfoWithMessage image userInteractionEnable 66 | 67 | @param message message 68 | @param image image 69 | @param userInteractionEnable userInteractionEnable 70 | */ 71 | + (void)showInfoWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable; 72 | 73 | /** 74 | showSuccessWithMessage 75 | 76 | @param message message 77 | */ 78 | + (void)showSuccessWithMessage:(NSString *)message; 79 | 80 | /** 81 | showSuccessWithMessage userInteractionEnable 82 | 83 | @param message message 84 | @param userInteractionEnable userInteractionEnable 85 | */ 86 | + (void)showSuccessWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 87 | 88 | /** 89 | showSuccessWithMessage image userInteractionEnable 90 | 91 | @param message message 92 | @param image image 93 | @param userInteractionEnable userInteractionEnable 94 | */ 95 | + (void)showSuccessWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable; 96 | 97 | /** 98 | showErrorWithMessage 99 | 100 | @param message message 101 | */ 102 | + (void)showErrorWithMessage:(NSString *)message; 103 | 104 | /** 105 | showErrorWithMessage userInteractionEnable 106 | 107 | @param message message 108 | @param userInteractionEnable userInteractionEnable 109 | */ 110 | + (void)showErrorWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 111 | 112 | /** 113 | showErrorWithMessage image userInteractionEnable 114 | 115 | @param message message 116 | @param image image 117 | @param userInteractionEnable userInteractionEnable 118 | */ 119 | + (void)showErrorWithMessage:(NSString *)message image:(UIImage *)image userInteractionEnable:(BOOL)userInteractionEnable; 120 | 121 | /** 122 | dismiss 123 | */ 124 | + (void)dismiss; 125 | 126 | /** 127 | setProgressIndicatorStyleToDefaultStyle 128 | */ 129 | + (void)setProgressIndicatorStyleToDefaultStyle; 130 | 131 | /** 132 | setProgressIndicatorStyle 133 | 134 | @param style style 135 | */ 136 | + (void)setProgressIndicatorStyle:(UIBlurEffectStyle)style; 137 | 138 | @end 139 | 140 | #pragma mark - FTProgressIndicatorView 141 | 142 | /** 143 | FTProgressIndicatorView 144 | */ 145 | @interface FTProgressIndicatorView : UIVisualEffectView 146 | 147 | /** 148 | userInteractionEnable, if allows user touches at view 149 | */ 150 | @property (assign, nonatomic) BOOL userInteractionEnable; 151 | 152 | /** 153 | showProgressWithType 154 | 155 | @param type type 156 | @param message message 157 | @param image custom image 158 | @param style style 159 | @param userInteractionEnable userInteractionEnable 160 | */ 161 | - (void)showProgressWithType:(FTProgressIndicatorMessageType )type message:(NSString *)message image:(UIImage *)image style:(UIBlurEffectStyle)style userInteractionEnable:(BOOL)userInteractionEnable; 162 | 163 | /** 164 | getFrameForProgressViewWithMessage 165 | 166 | @param progressMessage progressMessage 167 | @return CGSize 168 | */ 169 | - (CGSize )getFrameForProgressViewWithMessage:(NSString *)progressMessage; 170 | 171 | @end 172 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTToastIndicator/FTToastIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTToastIndicator.h 3 | // FTIndicator 4 | // 5 | // Created by liufengting on 16/7/26. 6 | // Copyright © 2016年 liufengting ( https://github.com/liufengting ). All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #pragma mark - FTToastIndicator 12 | /** 13 | FTToastIndicator 14 | */ 15 | @interface FTToastIndicator : NSObject 16 | /** 17 | setIndicatorStyleToDefaultStyle 18 | */ 19 | + (void)setToastIndicatorStyleToDefaultStyle; 20 | /** 21 | setIndicatorStyle 22 | 23 | @param style style 24 | */ 25 | + (void)setToastIndicatorStyle:(UIBlurEffectStyle)style; 26 | /** 27 | showToastMessage 28 | 29 | @param toastMessage toastMessage 30 | */ 31 | + (void)showToastMessage:(NSString *)toastMessage; 32 | 33 | /** 34 | dismiss 35 | */ 36 | + (void)dismiss; 37 | 38 | @end 39 | 40 | #pragma mark - FTToastIndicatorView 41 | 42 | /** 43 | FTToastIndicatorView 44 | */ 45 | @interface FTToastIndicatorView : UIVisualEffectView 46 | /** 47 | showToastMessage 48 | 49 | @param toastMessage toastMessage 50 | @param style style 51 | */ 52 | - (void)showToastMessage:(NSString *)toastMessage withStyle:(UIBlurEffectStyle)style; 53 | /** 54 | getFrameForToastViewWithMessage 55 | 56 | @param toastMessage toastMessage 57 | @return CGSize 58 | */ 59 | - (CGSize )getFrameForToastViewWithMessage:(NSString *)toastMessage; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Liu Fengting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demo/Pods/FTPickerView/FTPickerView/FTPickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTPickerView.h 3 | // FTPickerView 4 | // 5 | // Created by liufengting https://github.com/liufengting on 15/12/3. 6 | // Copyright © 2015年 liufengting. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^ FTPickerDoneBlock )(NSInteger selectedIndex); 12 | typedef void (^ FTPickerCancelBlock )(void); 13 | typedef void (^ FTDatePickerDoneBlock )(NSDate *selectedDate); 14 | typedef void (^ FTDatePickerCancelBlock )(void); 15 | 16 | /** 17 | * FTPickerTitleView 18 | */ 19 | 20 | @interface FTPickerTitleView : UIView 21 | 22 | @end 23 | 24 | /** 25 | * FTPickerView 26 | */ 27 | 28 | @interface FTPickerView : NSObject 29 | 30 | /** 31 | * show method 32 | * 33 | * @param title title 34 | * @param nameArray nameArray 35 | * @param doneBlock FTPickerDoneBlock 36 | * @param cancelBlock FTPickerCancelBlock 37 | */ 38 | +(void)showWithTitle:(NSString *)title 39 | nameArray:(NSArray *)nameArray 40 | doneBlock :(FTPickerDoneBlock)doneBlock 41 | cancelBlock:(FTPickerCancelBlock)cancelBlock; 42 | /** 43 | * dismiss 44 | */ 45 | +(void)dismiss; 46 | 47 | @end 48 | 49 | /** 50 | * FTDatePickerView 51 | */ 52 | 53 | @interface FTDatePickerView : UIView 54 | 55 | /** 56 | * show method 57 | * 58 | * @param title title 59 | * @param doneBlock FTDatePickerDoneBlock 60 | * @param cancelBlock FTDatePickerCancelBlock 61 | */ 62 | +(void)showWithTitle:(NSString *)title 63 | doneBlock :(FTDatePickerDoneBlock)doneBlock 64 | cancelBlock:(FTDatePickerCancelBlock)cancelBlock; 65 | /** 66 | * show method 67 | * 68 | * @param title title 69 | * @param selectDate selectDate 70 | * @param datePickerMode datePickerMode 71 | * @param doneBlock FTDatePickerDoneBlock 72 | * @param cancelBlock FTDatePickerCancelBlock 73 | */ 74 | +(void)showWithTitle:(NSString *)title 75 | selectDate:(NSDate *)selectDate 76 | datePickerMode:(UIDatePickerMode )datePickerMode 77 | doneBlock :(FTDatePickerDoneBlock)doneBlock 78 | cancelBlock:(FTDatePickerCancelBlock)cancelBlock; 79 | /** 80 | * dismiss 81 | */ 82 | +(void)dismiss; 83 | 84 | @end 85 | -------------------------------------------------------------------------------- /Demo/Pods/FTPickerView/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 刘锋婷 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Demo/Pods/FTPickerView/README.md: -------------------------------------------------------------------------------- 1 | # FTPickerView 2 | [![Twitter](https://img.shields.io/badge/twitter-@liufengting-blue.svg?style=flat)](http://twitter.com/liufengting) 3 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/liufengting/FTPickerView/master/LICENSE) 4 | [![Version](https://img.shields.io/cocoapods/v/FTPickerView.svg?style=flat)](http://cocoapods.org/pods/FTPickerView) 5 | [![CI Status](http://img.shields.io/travis/liufengting/FTPickerView.svg?style=flat)](https://travis-ci.org/liufengting/FTPickerView) 6 | [![GitHub stars](https://img.shields.io/github/stars/liufengting/FTPickerView.svg)](https://github.com/liufengting/FTPickerView/stargazers) 7 | 8 | 9 | A simple UIPickerView/UIDatePicker wrapper. 10 | 11 | ## Features 12 | 13 | - singleton 14 | - block callbacks 15 | 16 | 17 | ## ScreenShots 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | ## Useage 28 | 29 | * Simple Picker 30 | 31 | ```objective-c 32 | //simple picker 33 | [FTPickerView showWithTitle:@"Choose a step" 34 | nameArray:self.optionArrayOne 35 | doneBlock:^(NSInteger selectedIndex) { 36 | [sender setTitle:_optionArrayOne[selectedIndex] forState:UIControlStateNormal]; 37 | } cancelBlock:^{ 38 | 39 | }]; 40 | ``` 41 | 42 | * Date Picker 43 | 44 | 45 | ```objective-c 46 | //date picker 47 | [FTDatePickerView showWithTitle:@"Choose a date" 48 | selectDate:nil 49 | datePickerMode:UIDatePickerModeDateAndTime 50 | doneBlock:^(NSDate *selectedDate) { 51 | NSDateFormatter *dateFormate = [[NSDateFormatter alloc]init]; 52 | [dateFormate setDateFormat:@"yyyy-MM-dd HH:mm:ss"]; 53 | [sender setTitle:[dateFormate stringFromDate:selectedDate] forState:UIControlStateNormal]; 54 | } cancelBlock:^{ 55 | 56 | }]; 57 | ``` 58 | 59 | # Installation 60 | 61 | ## Manual 62 | * Drag 'FTPickerView' file to you project, 63 | * Import 'FTPickerView.h', 64 | * Enjoy! 🍺 65 | 66 | ## Cocoapods 67 | 68 | * add the following line to you podFile,then `pod update` 69 | 70 | ``` 71 | pod 'FTPickerView', '~> 0.1.1' 72 | ``` 73 | 74 | ## License 75 | 76 | FTPickerView is available under the MIT license. See the LICENSE file for more info. 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Wei Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2018/3/17. 6 | // Copyright (c) 2018 Wei Wang 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | 26 | import Foundation 27 | 28 | class Box { 29 | let value: T 30 | 31 | init(_ value: T) { 32 | self.value = value 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/CacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CacheSerializer.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/02. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// An `CacheSerializer` would be used to convert some data to an image object for 30 | /// retrieving from disk cache and vice versa for storing to disk cache. 31 | public protocol CacheSerializer { 32 | 33 | /// Get the serialized data from a provided image 34 | /// and optional original data for caching to disk. 35 | /// 36 | /// 37 | /// - parameter image: The image needed to be serialized. 38 | /// - parameter original: The original data which is just downloaded. 39 | /// If the image is retrieved from cache instead of 40 | /// downloaded, it will be `nil`. 41 | /// 42 | /// - returns: A data which will be stored to cache, or `nil` when no valid 43 | /// data could be serialized. 44 | func data(with image: Image, original: Data?) -> Data? 45 | 46 | /// Get an image deserialized from provided data. 47 | /// 48 | /// - parameter data: The data from which an image should be deserialized. 49 | /// - parameter options: Options for deserialization. 50 | /// 51 | /// - returns: An image deserialized or `nil` when no valid image 52 | /// could be deserialized. 53 | func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? 54 | } 55 | 56 | 57 | /// `DefaultCacheSerializer` is a basic `CacheSerializer` used in default cache of 58 | /// Kingfisher. It could serialize and deserialize PNG, JEPG and GIF images. For 59 | /// image other than these formats, a normalized `pngRepresentation` will be used. 60 | public struct DefaultCacheSerializer: CacheSerializer { 61 | 62 | public static let `default` = DefaultCacheSerializer() 63 | private init() {} 64 | 65 | public func data(with image: Image, original: Data?) -> Data? { 66 | let imageFormat = original?.kf.imageFormat ?? .unknown 67 | 68 | let data: Data? 69 | switch imageFormat { 70 | case .PNG: data = image.kf.pngRepresentation() 71 | case .JPEG: data = image.kf.jpegRepresentation(compressionQuality: 1.0) 72 | case .GIF: data = image.kf.gifRepresentation() 73 | case .unknown: data = original ?? image.kf.normalized.kf.pngRepresentation() 74 | } 75 | 76 | return data 77 | } 78 | 79 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 80 | let options = options ?? KingfisherEmptyOptionsInfo 81 | return Kingfisher.image( 82 | data: data, 83 | scale: options.scaleFactor, 84 | preloadAllAnimationData: options.preloadAllAnimationData, 85 | onlyFirstFrame: options.onlyLoadFirstFrame) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Filter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Filter.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/08/31. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | 28 | 29 | import CoreImage 30 | import Accelerate 31 | 32 | // Reuse the same CI Context for all CI drawing. 33 | private let ciContext = CIContext(options: nil) 34 | 35 | /// Transformer method which will be used in to provide a `Filter`. 36 | public typealias Transformer = (CIImage) -> CIImage? 37 | 38 | /// Supply a filter to create an `ImageProcessor`. 39 | public protocol CIImageProcessor: ImageProcessor { 40 | var filter: Filter { get } 41 | } 42 | 43 | extension CIImageProcessor { 44 | public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? { 45 | switch item { 46 | case .image(let image): 47 | return image.kf.apply(filter) 48 | case .data(_): 49 | return (DefaultImageProcessor.default >> self).process(item: item, options: options) 50 | } 51 | } 52 | } 53 | 54 | /// Wrapper for a `Transformer` of CIImage filters. 55 | public struct Filter { 56 | 57 | let transform: Transformer 58 | 59 | public init(tranform: @escaping Transformer) { 60 | self.transform = tranform 61 | } 62 | 63 | /// Tint filter which will apply a tint color to images. 64 | public static var tint: (Color) -> Filter = { 65 | color in 66 | Filter { input in 67 | let colorFilter = CIFilter(name: "CIConstantColorGenerator")! 68 | colorFilter.setValue(CIColor(color: color), forKey: kCIInputColorKey) 69 | 70 | let colorImage = colorFilter.outputImage 71 | let filter = CIFilter(name: "CISourceOverCompositing")! 72 | filter.setValue(colorImage, forKey: kCIInputImageKey) 73 | filter.setValue(input, forKey: kCIInputBackgroundImageKey) 74 | #if swift(>=4.0) 75 | return filter.outputImage?.cropped(to: input.extent) 76 | #else 77 | return filter.outputImage?.cropping(to: input.extent) 78 | #endif 79 | } 80 | } 81 | 82 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 83 | 84 | /// Color control filter which will apply color control change to images. 85 | public static var colorControl: (ColorElement) -> Filter = { arg -> Filter in 86 | let (brightness, contrast, saturation, inputEV) = arg 87 | return Filter { input in 88 | let paramsColor = [kCIInputBrightnessKey: brightness, 89 | kCIInputContrastKey: contrast, 90 | kCIInputSaturationKey: saturation] 91 | 92 | let paramsExposure = [kCIInputEVKey: inputEV] 93 | #if swift(>=4.0) 94 | let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor) 95 | return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure) 96 | #else 97 | let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) 98 | return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) 99 | #endif 100 | } 101 | 102 | } 103 | } 104 | 105 | extension Kingfisher where Base: Image { 106 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 107 | /// 108 | /// - parameter filter: The filter used to transform `self`. 109 | /// 110 | /// - returns: A transformed image by input `Filter`. 111 | /// 112 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 113 | public func apply(_ filter: Filter) -> Image { 114 | 115 | guard let cgImage = cgImage else { 116 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 117 | return base 118 | } 119 | 120 | let inputImage = CIImage(cgImage: cgImage) 121 | guard let outputImage = filter.transform(inputImage) else { 122 | return base 123 | } 124 | 125 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 126 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 127 | return base 128 | } 129 | 130 | #if os(macOS) 131 | return fixedForRetinaPixel(cgImage: result, to: size) 132 | #else 133 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 134 | #endif 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/FormatIndicatedCacheSerializer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Junyu Kuang on 5/28/17. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// `FormatIndicatedCacheSerializer` let you indicate an image format for serialized caches. 30 | /// 31 | /// It could serialize and deserialize PNG, JEPG and GIF images. For 32 | /// image other than these formats, a normalized `pngRepresentation` will be used. 33 | /// 34 | /// Example: 35 | /// ```` 36 | /// private let profileImageSize = CGSize(width: 44, height: 44) 37 | /// 38 | /// private let imageProcessor = RoundCornerImageProcessor( 39 | /// cornerRadius: profileImageSize.width / 2, targetSize: profileImageSize) 40 | /// 41 | /// private let optionsInfo: KingfisherOptionsInfo = [ 42 | /// .cacheSerializer(FormatIndicatedCacheSerializer.png), 43 | /// .backgroundDecode, .processor(imageProcessor), .scaleFactor(UIScreen.main.scale)] 44 | /// 45 | /// extension UIImageView { 46 | /// func setProfileImage(with url: URL) { 47 | /// // Image will always cached as PNG format to preserve alpha channel for round rect. 48 | /// _ = kf.setImage(with: url, options: optionsInfo) 49 | /// } 50 | ///} 51 | /// ```` 52 | public struct FormatIndicatedCacheSerializer: CacheSerializer { 53 | 54 | public static let png = FormatIndicatedCacheSerializer(imageFormat: .PNG) 55 | public static let jpeg = FormatIndicatedCacheSerializer(imageFormat: .JPEG) 56 | public static let gif = FormatIndicatedCacheSerializer(imageFormat: .GIF) 57 | 58 | /// The indicated image format. 59 | private let imageFormat: ImageFormat 60 | 61 | public func data(with image: Image, original: Data?) -> Data? { 62 | 63 | func imageData(withFormat imageFormat: ImageFormat) -> Data? { 64 | switch imageFormat { 65 | case .PNG: return image.kf.pngRepresentation() 66 | case .JPEG: return image.kf.jpegRepresentation(compressionQuality: 1.0) 67 | case .GIF: return image.kf.gifRepresentation() 68 | case .unknown: return nil 69 | } 70 | } 71 | 72 | // generate data with indicated image format 73 | if let data = imageData(withFormat: imageFormat) { 74 | return data 75 | } 76 | 77 | let originalFormat = original?.kf.imageFormat ?? .unknown 78 | 79 | // generate data with original image's format 80 | if originalFormat != imageFormat, let data = imageData(withFormat: originalFormat) { 81 | return data 82 | } 83 | 84 | return original ?? image.kf.normalized.kf.pngRepresentation() 85 | } 86 | 87 | /// Same implementation as `DefaultCacheSerializer`. 88 | public func image(with data: Data, options: KingfisherOptionsInfo?) -> Image? { 89 | let options = options ?? KingfisherEmptyOptionsInfo 90 | return Kingfisher.image( 91 | data: data, 92 | scale: options.scaleFactor, 93 | preloadAllAnimationData: options.preloadAllAnimationData, 94 | onlyFirstFrame: options.onlyLoadFirstFrame) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/ImageTransition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImageTransition.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/9/18. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | // Not implemented for macOS and watchOS yet. 29 | 30 | import AppKit 31 | 32 | /// Image transition is not supported on macOS. 33 | public enum ImageTransition { 34 | case none 35 | var duration: TimeInterval { 36 | return 0 37 | } 38 | } 39 | 40 | #elseif os(watchOS) 41 | import UIKit 42 | /// Image transition is not supported on watchOS. 43 | public enum ImageTransition { 44 | case none 45 | var duration: TimeInterval { 46 | return 0 47 | } 48 | } 49 | #else 50 | import UIKit 51 | 52 | /** 53 | Transition effect which will be used when an image downloaded and set by `UIImageView` extension API in Kingfisher. 54 | You can assign an enum value with transition duration as an item in `KingfisherOptionsInfo` 55 | to enable the animation transition. 56 | 57 | Apple's UIViewAnimationOptions is used under the hood. 58 | For custom transition, you should specified your own transition options, animations and 59 | comletion handler as well. 60 | */ 61 | public enum ImageTransition { 62 | /// No animation transistion. 63 | case none 64 | 65 | /// Fade in the loaded image. 66 | case fade(TimeInterval) 67 | 68 | /// Flip from left transition. 69 | case flipFromLeft(TimeInterval) 70 | 71 | /// Flip from right transition. 72 | case flipFromRight(TimeInterval) 73 | 74 | /// Flip from top transition. 75 | case flipFromTop(TimeInterval) 76 | 77 | /// Flip from bottom transition. 78 | case flipFromBottom(TimeInterval) 79 | 80 | /// Custom transition. 81 | case custom(duration: TimeInterval, 82 | options: UIViewAnimationOptions, 83 | animations: ((UIImageView, UIImage) -> Void)?, 84 | completion: ((Bool) -> Void)?) 85 | 86 | var duration: TimeInterval { 87 | switch self { 88 | case .none: return 0 89 | case .fade(let duration): return duration 90 | 91 | case .flipFromLeft(let duration): return duration 92 | case .flipFromRight(let duration): return duration 93 | case .flipFromTop(let duration): return duration 94 | case .flipFromBottom(let duration): return duration 95 | 96 | case .custom(let duration, _, _, _): return duration 97 | } 98 | } 99 | 100 | var animationOptions: UIViewAnimationOptions { 101 | switch self { 102 | case .none: return [] 103 | case .fade(_): return .transitionCrossDissolve 104 | 105 | case .flipFromLeft(_): return .transitionFlipFromLeft 106 | case .flipFromRight(_): return .transitionFlipFromRight 107 | case .flipFromTop(_): return .transitionFlipFromTop 108 | case .flipFromBottom(_): return .transitionFlipFromBottom 109 | 110 | case .custom(_, let options, _, _): return options 111 | } 112 | } 113 | 114 | var animations: ((UIImageView, UIImage) -> Void)? { 115 | switch self { 116 | case .custom(_, _, let animations, _): return animations 117 | default: return { $0.image = $1 } 118 | } 119 | } 120 | 121 | var completion: ((Bool) -> Void)? { 122 | switch self { 123 | case .custom(_, _, _, let completion): return completion 124 | default: return nil 125 | } 126 | } 127 | } 128 | #endif 129 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Kingfisher.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.h 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #import 28 | 29 | //! Project version number for Kingfisher. 30 | FOUNDATION_EXPORT double KingfisherVersionNumber; 31 | 32 | //! Project version string for Kingfisher. 33 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 34 | 35 | // In this header, you should import all the public headers of your framework using statements like #import 36 | 37 | 38 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Kingfisher.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Kingfisher.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 16/9/14. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | import ImageIO 29 | 30 | #if os(macOS) 31 | import AppKit 32 | public typealias Image = NSImage 33 | public typealias View = NSView 34 | public typealias Color = NSColor 35 | public typealias ImageView = NSImageView 36 | public typealias Button = NSButton 37 | #else 38 | import UIKit 39 | public typealias Image = UIImage 40 | public typealias Color = UIColor 41 | #if !os(watchOS) 42 | public typealias ImageView = UIImageView 43 | public typealias View = UIView 44 | public typealias Button = UIButton 45 | #endif 46 | #endif 47 | 48 | public final class Kingfisher { 49 | public let base: Base 50 | public init(_ base: Base) { 51 | self.base = base 52 | } 53 | } 54 | 55 | /** 56 | A type that has Kingfisher extensions. 57 | */ 58 | public protocol KingfisherCompatible { 59 | associatedtype CompatibleType 60 | var kf: CompatibleType { get } 61 | } 62 | 63 | public extension KingfisherCompatible { 64 | public var kf: Kingfisher { 65 | get { return Kingfisher(self) } 66 | } 67 | } 68 | 69 | extension Image: KingfisherCompatible { } 70 | #if !os(watchOS) 71 | extension ImageView: KingfisherCompatible { } 72 | extension Button: KingfisherCompatible { } 73 | #endif 74 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Placeholder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Placeholder.swift 3 | // Kingfisher 4 | // 5 | // Created by Tieme van Veen on 28/08/2017. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | #if os(macOS) 28 | import AppKit 29 | #else 30 | import UIKit 31 | #endif 32 | 33 | 34 | /// Represent a placeholder type which could be set while loading as well as 35 | /// loading finished without getting an image. 36 | public protocol Placeholder { 37 | 38 | /// How the placeholder should be added to a given image view. 39 | func add(to imageView: ImageView) 40 | 41 | /// How the placeholder should be removed from a given image view. 42 | func remove(from imageView: ImageView) 43 | } 44 | 45 | /// Default implementation of an image placeholder. The image will be set or 46 | /// reset directly for `image` property of the image view. 47 | extension Placeholder where Self: Image { 48 | 49 | /// How the placeholder should be added to a given image view. 50 | public func add(to imageView: ImageView) { imageView.image = self } 51 | 52 | /// How the placeholder should be removed from a given image view. 53 | public func remove(from imageView: ImageView) { imageView.image = nil } 54 | } 55 | 56 | extension Image: Placeholder {} 57 | 58 | /// Default implementation of an arbitrary view as placeholder. The view will be 59 | /// added as a subview when adding and be removed from its super view when removing. 60 | /// 61 | /// To use your customize View type as placeholder, simply let it conforming to 62 | /// `Placeholder` by `extension MyView: Placeholder {}`. 63 | extension Placeholder where Self: View { 64 | 65 | /// How the placeholder should be added to a given image view. 66 | public func add(to imageView: ImageView) { 67 | imageView.addSubview(self) 68 | 69 | self.translatesAutoresizingMaskIntoConstraints = false 70 | NSLayoutConstraint.activate([ 71 | NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0), 72 | NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0), 73 | NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0), 74 | NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0) 75 | ]) 76 | } 77 | 78 | /// How the placeholder should be removed from a given image view. 79 | public func remove(from imageView: ImageView) { 80 | self.removeFromSuperview() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/RequestModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestModifier.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 2016/09/05. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | /// Request modifier of image downloader. 30 | public protocol ImageDownloadRequestModifier { 31 | func modified(for request: URLRequest) -> URLRequest? 32 | } 33 | 34 | struct NoModifier: ImageDownloadRequestModifier { 35 | static let `default` = NoModifier() 36 | private init() {} 37 | func modified(for request: URLRequest) -> URLRequest? { 38 | return request 39 | } 40 | } 41 | 42 | public struct AnyModifier: ImageDownloadRequestModifier { 43 | 44 | let block: (URLRequest) -> URLRequest? 45 | 46 | public func modified(for request: URLRequest) -> URLRequest? { 47 | return block(request) 48 | } 49 | 50 | public init(modify: @escaping (URLRequest) -> URLRequest? ) { 51 | block = modify 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resource.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/4/6. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | 30 | /// `Resource` protocol defines how to download and cache a resource from network. 31 | public protocol Resource { 32 | /// The key used in cache. 33 | var cacheKey: String { get } 34 | 35 | /// The target image URL. 36 | var downloadURL: URL { get } 37 | } 38 | 39 | /** 40 | ImageResource is a simple combination of `downloadURL` and `cacheKey`. 41 | 42 | When passed to image view set methods, Kingfisher will try to download the target 43 | image from the `downloadURL`, and then store it with the `cacheKey` as the key in cache. 44 | */ 45 | public struct ImageResource: Resource { 46 | /// The key used in cache. 47 | public let cacheKey: String 48 | 49 | /// The target image URL. 50 | public let downloadURL: URL 51 | 52 | /** 53 | Create a resource. 54 | 55 | - parameter downloadURL: The target image URL. 56 | - parameter cacheKey: The cache key. If `nil`, Kingfisher will use the `absoluteString` of `downloadURL` as the key. 57 | 58 | - returns: A resource. 59 | */ 60 | public init(downloadURL: URL, cacheKey: String? = nil) { 61 | self.downloadURL = downloadURL 62 | self.cacheKey = cacheKey ?? downloadURL.absoluteString 63 | } 64 | } 65 | 66 | /** 67 | URL conforms to `Resource` in Kingfisher. 68 | The `absoluteString` of this URL is used as `cacheKey`. And the URL itself will be used as `downloadURL`. 69 | If you need customize the url and/or cache key, use `ImageResource` instead. 70 | */ 71 | extension URL: Resource { 72 | public var cacheKey: String { return absoluteString } 73 | public var downloadURL: URL { return self } 74 | } 75 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/ThreadHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadHelper.swift 3 | // Kingfisher 4 | // 5 | // Created by Wei Wang on 15/10/9. 6 | // 7 | // Copyright (c) 2018 Wei Wang 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE. 26 | 27 | import Foundation 28 | 29 | extension DispatchQueue { 30 | // This method will dispatch the `block` to self. 31 | // If `self` is the main queue, and current thread is main thread, the block 32 | // will be invoked immediately instead of being dispatched. 33 | func safeAsync(_ block: @escaping ()->()) { 34 | if self === DispatchQueue.main && Thread.isMainThread { 35 | block() 36 | } else { 37 | async { block() } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Demo/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FTImageSize (0.0.4) 3 | - FTIndicator (1.2.9): 4 | - FTIndicator/FTNotificationIndicator (= 1.2.9) 5 | - FTIndicator/FTProgressIndicator (= 1.2.9) 6 | - FTIndicator/FTToastIndicator (= 1.2.9) 7 | - FTIndicator/FTNotificationIndicator (1.2.9) 8 | - FTIndicator/FTProgressIndicator (1.2.9) 9 | - FTIndicator/FTToastIndicator (1.2.9) 10 | - FTPickerView (1.0.2) 11 | - Kingfisher (4.7.0) 12 | 13 | DEPENDENCIES: 14 | - FTImageSize 15 | - FTIndicator 16 | - FTPickerView 17 | - Kingfisher 18 | 19 | SPEC CHECKSUMS: 20 | FTImageSize: 29c6fc0830dbe33973286148a8e32cd582a9f26d 21 | FTIndicator: f7f071fd159e5befa1d040a9ef2e3ab53fa9322c 22 | FTPickerView: 3fb6055e057ac608435ce7d938f2bbc0640f4948 23 | Kingfisher: da6b005aa96d37698e3e4f1ccfe96a5b9bbf27d6 24 | 25 | PODFILE CHECKSUM: 0897bbd78b2258d0c7f01ee2e9d5eb5a802cde17 26 | 27 | COCOAPODS: 1.4.0 28 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FTImageSize : NSObject 3 | @end 4 | @implementation PodsDummy_FTImageSize 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double FTImageSizeVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char FTImageSizeVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize.modulemap: -------------------------------------------------------------------------------- 1 | framework module FTImageSize { 2 | umbrella header "FTImageSize-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FTImageSize 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FTImageSize 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | SWIFT_VERSION = 3.0 12 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.0.4 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FTIndicator : NSObject 3 | @end 4 | @implementation PodsDummy_FTIndicator 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "FTIndicator.h" 14 | #import "FTNotificationIndicator.h" 15 | #import "FTProgressIndicator.h" 16 | #import "FTToastIndicator.h" 17 | #import "FTNotificationIndicator.h" 18 | #import "FTProgressIndicator.h" 19 | #import "FTToastIndicator.h" 20 | 21 | FOUNDATION_EXPORT double FTIndicatorVersionNumber; 22 | FOUNDATION_EXPORT const unsigned char FTIndicatorVersionString[]; 23 | 24 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator.modulemap: -------------------------------------------------------------------------------- 1 | framework module FTIndicator { 2 | umbrella header "FTIndicator-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FTIndicator 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FTIndicator 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2.9 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_FTPickerView : NSObject 3 | @end 4 | @implementation PodsDummy_FTPickerView 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "FTPickerView.h" 14 | 15 | FOUNDATION_EXPORT double FTPickerViewVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char FTPickerViewVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView.modulemap: -------------------------------------------------------------------------------- 1 | framework module FTPickerView { 2 | umbrella header "FTPickerView-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FTPickerView 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/FTPickerView 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.7.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Kingfisher : NSObject 3 | @end 4 | @implementation PodsDummy_Kingfisher 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "Kingfisher.h" 14 | 15 | FOUNDATION_EXPORT double KingfisherVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher.modulemap: -------------------------------------------------------------------------------- 1 | framework module Kingfisher { 2 | umbrella header "Kingfisher-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "CFNetwork" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Kingfisher 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## FTImageSize 5 | 6 | MIT License 7 | 8 | Copyright (c) 2016 Liu Fengting 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | 29 | ## FTIndicator 30 | 31 | The MIT License (MIT) 32 | 33 | Copyright (c) 2016 Liu Fengting 34 | 35 | Permission is hereby granted, free of charge, to any person obtaining a copy 36 | of this software and associated documentation files (the "Software"), to deal 37 | in the Software without restriction, including without limitation the rights 38 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 39 | copies of the Software, and to permit persons to whom the Software is 40 | furnished to do so, subject to the following conditions: 41 | 42 | The above copyright notice and this permission notice shall be included in all 43 | copies or substantial portions of the Software. 44 | 45 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 46 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 47 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 48 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 49 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 50 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 51 | SOFTWARE. 52 | 53 | 54 | ## FTPickerView 55 | 56 | The MIT License (MIT) 57 | 58 | Copyright (c) 2016 刘锋婷 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in all 68 | copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 76 | SOFTWARE. 77 | 78 | 79 | ## Kingfisher 80 | 81 | The MIT License (MIT) 82 | 83 | Copyright (c) 2018 Wei Wang 84 | 85 | Permission is hereby granted, free of charge, to any person obtaining a copy 86 | of this software and associated documentation files (the "Software"), to deal 87 | in the Software without restriction, including without limitation the rights 88 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 89 | copies of the Software, and to permit persons to whom the Software is 90 | furnished to do so, subject to the following conditions: 91 | 92 | The above copyright notice and this permission notice shall be included in all 93 | copies or substantial portions of the Software. 94 | 95 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 96 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 97 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 98 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 99 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 100 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 101 | SOFTWARE. 102 | 103 | 104 | Generated by CocoaPods - https://cocoapods.org 105 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Demo : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Demo 5 | @end 6 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DemoVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DemoVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FTImageSize" "${PODS_CONFIGURATION_BUILD_DIR}/FTIndicator" "${PODS_CONFIGURATION_BUILD_DIR}/FTPickerView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTImageSize/FTImageSize.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTIndicator/FTIndicator.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTPickerView/FTPickerView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "FTImageSize" -framework "FTIndicator" -framework "FTPickerView" -framework "Kingfisher" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Demo { 2 | umbrella header "Pods-Demo-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FTImageSize" "${PODS_CONFIGURATION_BUILD_DIR}/FTIndicator" "${PODS_CONFIGURATION_BUILD_DIR}/FTPickerView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTImageSize/FTImageSize.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTIndicator/FTIndicator.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FTPickerView/FTPickerView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "FTImageSize" -framework "FTIndicator" -framework "FTPickerView" -framework "Kingfisher" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/FTChatMessage/FTChatMessageCell/.DS_Store -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageBubbleItem/FTChatMessageBubbleAudioItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageBubbleAudioItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/5/7. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageBubbleAudioItem: FTChatMessageBubbleItem { 12 | 13 | var playImageView : UIImageView! 14 | var mediaInfoLabel : UILabel! 15 | 16 | convenience init(frame: CGRect, aMessage : FTChatMessageModel, for indexPath: IndexPath) { 17 | self.init(frame:frame) 18 | self.backgroundColor = UIColor.clear 19 | message = aMessage 20 | let messageBubblePath = self.getAudioBubblePath(frame.size, isUserSelf: aMessage.isUserSelf) 21 | 22 | messageBubbleLayer.path = messageBubblePath.cgPath 23 | messageBubbleLayer.fillColor = aMessage.messageSender.isUserSelf ? FTDefaultOutgoingColor.cgColor : FTDefaultIncomingColor.cgColor 24 | self.layer.addSublayer(messageBubbleLayer) 25 | 26 | let mediaImageRect = self.getPlayImageViewFrame(aMessage.isUserSelf) 27 | playImageView = UIImageView(frame : mediaImageRect) 28 | playImageView.backgroundColor = UIColor.clear 29 | playImageView.tintColor = aMessage.messageSender.isUserSelf ? UIColor.white : UIColor.black 30 | if let image = UIImage(named: "Media_Play") { 31 | playImageView.image = image.withRenderingMode(UIImageRenderingMode.alwaysTemplate) 32 | } 33 | self.addSubview(playImageView) 34 | 35 | let mediaInfoLabelRect = self.getMediaInfoLabelFrame(aMessage.isUserSelf) 36 | mediaInfoLabel = UILabel(frame : mediaInfoLabelRect) 37 | mediaInfoLabel.backgroundColor = UIColor.clear 38 | mediaInfoLabel.font = FTDefaultFontSize 39 | mediaInfoLabel.textColor = aMessage.messageSender.isUserSelf ? UIColor.white : UIColor.black 40 | mediaInfoLabel.textAlignment = aMessage.isUserSelf ? NSTextAlignment.left : NSTextAlignment.right 41 | mediaInfoLabel.text = "1′ 22″" 42 | self.addSubview(mediaInfoLabel) 43 | 44 | } 45 | 46 | fileprivate func getAudioBubblePath(_ size:CGSize , isUserSelf : Bool) -> UIBezierPath { 47 | let bubbleRect = CGRect(x: isUserSelf ? 0 : FTDefaultMessageBubbleAngleWidth, y: 0, width: size.width - FTDefaultMessageBubbleAngleWidth , height: size.height) 48 | let path = UIBezierPath.init(roundedRect: bubbleRect, cornerRadius: size.height/2) 49 | return path; 50 | } 51 | 52 | fileprivate func getPlayImageViewFrame(_ isUserSelf : Bool) -> CGRect { 53 | let margin = (FTDefaultMessageBubbleAudioHeight - FTDefaultMessageBubbleAudioIconHeight)/2 54 | return isUserSelf ? 55 | CGRect(x: margin, y: margin, width: FTDefaultMessageBubbleAudioIconHeight, height: FTDefaultMessageBubbleAudioIconHeight) : 56 | CGRect(x: self.frame.size.width - FTDefaultMessageBubbleAudioHeight + margin , y: margin, width: FTDefaultMessageBubbleAudioIconHeight, height: FTDefaultMessageBubbleAudioIconHeight) 57 | 58 | } 59 | fileprivate func getMediaInfoLabelFrame(_ isUserSelf : Bool) -> CGRect { 60 | let margin = (FTDefaultMessageBubbleAudioHeight - FTDefaultMessageBubbleAudioIconHeight)/2 61 | return isUserSelf ? 62 | CGRect(x: FTDefaultMessageBubbleAudioHeight, y: margin, width: self.frame.size.width - FTDefaultMessageBubbleAudioHeight - FTDefaultMessageBubbleAngleWidth - margin, height: FTDefaultMessageBubbleAudioIconHeight) : 63 | CGRect( x: FTDefaultMessageBubbleAngleWidth + margin, y: margin, width: self.frame.size.width - FTDefaultMessageBubbleAudioHeight - FTDefaultMessageBubbleAngleWidth - margin, height: FTDefaultMessageBubbleAudioIconHeight) 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageBubbleItem/FTChatMessageBubbleImageItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageBubbleImageItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/5/7. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Kingfisher 11 | 12 | class FTChatMessageBubbleImageItem: FTChatMessageBubbleItem { 13 | 14 | convenience init(frame: CGRect, aMessage : FTChatMessageModel, for indexPath: IndexPath) { 15 | self.init(frame:frame) 16 | self.backgroundColor = UIColor.clear 17 | message = aMessage 18 | 19 | let messageBubblePath = self.getBubbleShapePathWithSize(frame.size, isUserSelf: aMessage.isUserSelf, for: indexPath) 20 | 21 | let maskLayer = CAShapeLayer() 22 | maskLayer.path = messageBubblePath.cgPath 23 | maskLayer.frame = self.bounds 24 | 25 | let layer = CALayer() 26 | layer.mask = maskLayer 27 | layer.frame = self.bounds 28 | layer.contentsScale = UIScreen.main.scale 29 | layer.contentsGravity = kCAGravityResizeAspectFill 30 | layer.backgroundColor = aMessage.messageSender.isUserSelf ? FTDefaultOutgoingColor.cgColor : FTDefaultIncomingColor.cgColor 31 | self.layer.addSublayer(layer) 32 | 33 | if aMessage.isKind(of: FTChatMessageImageModel.classForCoder()) { 34 | if let image : UIImage = (aMessage as! FTChatMessageImageModel).image { 35 | layer.contents = image.withRenderingMode(.alwaysOriginal).cgImage 36 | }else if let imageURL : String = (aMessage as! FTChatMessageImageModel).imageUrl { 37 | ImageDownloader.default.downloadImage(with: URL(string: imageURL)!, options: [], progressBlock: nil) { 38 | (image, error, url, data) in 39 | if image != nil { 40 | layer.contents = image?.cgImage 41 | } 42 | } 43 | } 44 | }else{ 45 | if let image = UIImage(named : "dog.jpg") { 46 | layer.contents = image.cgImage 47 | } 48 | } 49 | } 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageBubbleItem/FTChatMessageBubbleLocationItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageBubbleLocationItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/5/7. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class FTChatMessageBubbleLocationItem: FTChatMessageBubbleItem { 13 | 14 | var mapView : MKMapView! 15 | 16 | convenience init(frame: CGRect, aMessage : FTChatMessageModel, for indexPath: IndexPath) { 17 | self.init(frame:frame) 18 | self.backgroundColor = UIColor.clear 19 | message = aMessage 20 | 21 | let mapRect = self.getMapSize(aMessage.isUserSelf) 22 | mapView = MKMapView(frame : mapRect) 23 | mapView.isScrollEnabled = false 24 | mapView.isUserInteractionEnabled = false 25 | mapView.layer.cornerRadius = FTDefaultMessageRoundCorner 26 | mapView.layer.borderColor = aMessage.messageSender.isUserSelf ? FTDefaultOutgoingColor.cgColor : FTDefaultIncomingColor.cgColor 27 | mapView.layer.borderWidth = 0.8 28 | self.addSubview(mapView) 29 | 30 | 31 | // setRegion 32 | let coordinate = CLLocationCoordinate2DMake(31.479461477978905, 120.38549624655187); 33 | let region = MKCoordinateRegionMakeWithDistance(coordinate, Double(mapRect.size.width), Double(mapRect.size.height)) 34 | mapView.setRegion(region, animated: false) 35 | // addAnnotation 36 | let string = NSString(format: "%.1f,%.1f",coordinate.latitude,coordinate.longitude) 37 | let pin = MapPin.init(coordinate: coordinate, title: "My Location", subtitle: string as String) 38 | mapView.addAnnotation(pin as MKAnnotation) 39 | 40 | } 41 | 42 | 43 | fileprivate func getMapSize(_ isUserSelf : Bool) -> CGRect { 44 | let bubbleRect = CGRect(x: isUserSelf ? 0 : FTDefaultMessageBubbleAngleWidth, y: 0, width: self.bounds.size.width - FTDefaultMessageBubbleAngleWidth , height: self.bounds.size.height) 45 | return bubbleRect; 46 | } 47 | 48 | 49 | 50 | } 51 | 52 | 53 | class MapPin : NSObject, MKAnnotation { 54 | var coordinate: CLLocationCoordinate2D 55 | var title: String? 56 | var subtitle: String? 57 | 58 | init(coordinate: CLLocationCoordinate2D, title: String, subtitle: String) { 59 | self.coordinate = coordinate 60 | self.title = title 61 | self.subtitle = subtitle 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageBubbleItem/FTChatMessageBubbleTextItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageBubbleTextItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/5/7. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageBubbleTextItem: FTChatMessageBubbleItem { 12 | 13 | convenience init(frame: CGRect, aMessage : FTChatMessageModel, for indexPath: IndexPath) { 14 | self.init(frame:frame) 15 | self.backgroundColor = UIColor.clear 16 | message = aMessage 17 | 18 | let messageBubblePath = self.getBubbleShapePathWithSize(frame.size, isUserSelf: aMessage.isUserSelf, for: indexPath) 19 | 20 | messageBubbleLayer.path = messageBubblePath.cgPath 21 | messageBubbleLayer.fillColor = aMessage.messageSender.isUserSelf ? FTDefaultOutgoingColor.cgColor : FTDefaultIncomingColor.cgColor 22 | self.layer.addSublayer(messageBubbleLayer) 23 | 24 | 25 | //text 26 | messageLabel = UILabel(frame: self.getTextRectWithSize(frame.size, isUserSelf: aMessage.isUserSelf)); 27 | messageLabel.text = message.messageText 28 | messageLabel.numberOfLines = 0 29 | messageLabel.textColor = aMessage.messageSender.isUserSelf ? UIColor.white : UIColor.black 30 | messageLabel.font = FTDefaultFontSize 31 | self.addSubview(messageLabel) 32 | let attributeString = NSMutableAttributedString(attributedString: messageLabel.attributedText!) 33 | attributeString.addAttributes([NSAttributedStringKey.font:FTDefaultFontSize,NSAttributedStringKey.paragraphStyle: FTChatMessagePublicMethods.getFTDefaultMessageParagraphStyle()], range: NSMakeRange(0, (messageLabel.text! as NSString).length)) 34 | messageLabel.attributedText = attributeString 35 | 36 | } 37 | 38 | fileprivate func getTextRectWithSize(_ size:CGSize , isUserSelf : Bool) -> CGRect { 39 | let bubbleWidth = size.width - FTDefaultMessageBubbleAngleWidth - FTDefaultTextLeftMargin*2 40 | let bubbleHeight = size.height - FTDefaultTextTopMargin*2 41 | let y = FTDefaultTextTopMargin 42 | let x : CGFloat = isUserSelf ? FTDefaultTextLeftMargin : FTDefaultMessageBubbleAngleWidth + FTDefaultTextLeftMargin 43 | return CGRect(x: x,y: y,width: bubbleWidth,height: bubbleHeight); 44 | } 45 | 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageBubbleItem/FTChatMessageBubbleVideoItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageBubbleVideoItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/5/7. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageBubbleVideoItem: FTChatMessageBubbleItem { 12 | 13 | var mediaPlayImageView : UIImageView! 14 | 15 | 16 | convenience init(frame: CGRect, aMessage : FTChatMessageModel, for indexPath: IndexPath) { 17 | self.init(frame:frame) 18 | self.backgroundColor = UIColor.clear 19 | message = aMessage 20 | let messageBubblePath = self.getBubbleShapePathWithSize(frame.size, isUserSelf: aMessage.isUserSelf, for: indexPath) 21 | 22 | 23 | let maskLayer = CAShapeLayer() 24 | maskLayer.path = messageBubblePath.cgPath 25 | maskLayer.frame = self.bounds 26 | maskLayer.contentsScale = UIScreen.main.scale; 27 | 28 | let layer = CAShapeLayer() 29 | layer.mask = maskLayer 30 | layer.frame = self.bounds 31 | self.layer.addSublayer(layer) 32 | 33 | if let image = UIImage(named : "dog.jpg") { 34 | layer.contents = image.cgImage 35 | } 36 | 37 | 38 | let mediaImageRect = self.getMediaImageViewFrame(aMessage.isUserSelf) 39 | 40 | mediaPlayImageView = UIImageView(frame : mediaImageRect) 41 | mediaPlayImageView.backgroundColor = UIColor.clear 42 | mediaPlayImageView.image = UIImage(named: "Media_Play") 43 | self.addSubview(mediaPlayImageView) 44 | } 45 | 46 | fileprivate func getMediaImageViewFrame(_ isUserSelf : Bool) -> CGRect { 47 | let xx = isUserSelf ? 48 | (self.frame.size.width - FTDefaultMessageBubbleAngleWidth - FTDefaultMessageBubbleMediaIconHeight)/2 : 49 | FTDefaultMessageBubbleAngleWidth + (self.frame.size.width - FTDefaultMessageBubbleAngleWidth - FTDefaultMessageBubbleMediaIconHeight)/2 50 | let yy = (self.frame.size.height - FTDefaultMessageBubbleMediaIconHeight)/2 51 | return CGRect(x: xx, y: yy, width: FTDefaultMessageBubbleMediaIconHeight, height: FTDefaultMessageBubbleMediaIconHeight) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageCell.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //MARK: - FTChatMessageCell 12 | 13 | class FTChatMessageCell: UITableViewCell { 14 | 15 | var message : FTChatMessageModel! 16 | var messageBubbleItem: FTChatMessageBubbleItem! 17 | 18 | //MARK: - messageTimeLabel 19 | lazy var messageTimeLabel: UILabel! = { 20 | let label = UILabel(frame: CGRect.zero) 21 | label.font = FTDefaultTimeLabelFont 22 | label.textAlignment = .center 23 | label.textColor = UIColor.lightGray 24 | return label 25 | }() 26 | 27 | 28 | //MARK: - messageDeliverStatusView 29 | var messageDeliverStatusView : FTChatMessageDeliverStatusView? = { 30 | return FTChatMessageDeliverStatusView(frame: CGRect.zero) 31 | }() 32 | 33 | //MARK: - convenience init 34 | convenience init(style: UITableViewCellStyle, reuseIdentifier: String?, theMessage : FTChatMessageModel, for indexPath: IndexPath) { 35 | self.init(style: style, reuseIdentifier: reuseIdentifier) 36 | 37 | message = theMessage 38 | 39 | let heightSoFar : CGFloat = 0 40 | var bubbleRect = CGRect.zero 41 | 42 | 43 | if indexPath.row == 0 { 44 | self.addTimeLabel() 45 | } 46 | 47 | let y : CGFloat = heightSoFar 48 | let bubbleWidth : CGFloat = FTChatMessageBubbleItem.getMessageBubbleWidthForMessage(theMessage) 49 | let bubbleHeight : CGFloat = FTChatMessageBubbleItem.getMessageBubbleHeightForMessage(theMessage) 50 | 51 | let x = theMessage.isUserSelf ? FTScreenWidth - (FTDefaultIconSize + FTDefaultMargin + FTDefaultMessageCellIconToMessageMargin) - bubbleWidth : FTDefaultIconSize + FTDefaultMargin + FTDefaultMessageCellIconToMessageMargin 52 | 53 | bubbleRect = CGRect(x: x, y: y, width: bubbleWidth, height: bubbleHeight) 54 | 55 | self.setupCellBubbleItem(bubbleRect, for: indexPath) 56 | 57 | } 58 | 59 | //MARK: - setupCellBubbleItem 60 | func setupCellBubbleItem(_ bubbleFrame: CGRect, for indexPath: IndexPath) { 61 | 62 | messageBubbleItem = FTChatMessageBubbleItem.getBubbleItemWithFrame(bubbleFrame, aMessage: message, for: indexPath) 63 | messageBubbleItem.addTarget(self, action: #selector(self.itemTapped), for: UIControlEvents.touchUpInside) 64 | self.addSubview(messageBubbleItem) 65 | 66 | if message.isUserSelf && message.messageDeliverStatus != FTChatMessageDeliverStatus.succeeded{ 67 | self.addSendStatusView(bubbleFrame) 68 | } 69 | } 70 | 71 | //MARK: - addTimeLabel 72 | func addTimeLabel() { 73 | let timeLabelRect = CGRect(x: 0, y: -FTDefaultMessageCellTimeLabelHeight ,width: FTScreenWidth, height: FTDefaultMessageCellTimeLabelHeight); 74 | messageTimeLabel.frame = timeLabelRect 75 | messageTimeLabel.text = message.messageTimeStamp 76 | self.addSubview(messageTimeLabel) 77 | } 78 | 79 | //MARK: - addSenderLabel 80 | // func addSenderLabel() { 81 | // var nameLabelTextAlignment : NSTextAlignment = .right 82 | // var nameLabelRect = CGRect( x: 0, y: -FTDefaultSectionHeight/2 , width: FTScreenWidth - (FTDefaultMargin + FTDefaultIconSize + FTDefaultMessageBubbleAngleWidth), height: FTDefaultSectionHeight/2) 83 | // 84 | // if message.isUserSelf == false { 85 | // nameLabelRect.origin.x = FTDefaultMargin + FTDefaultIconSize + FTDefaultMessageBubbleAngleWidth 86 | // nameLabelTextAlignment = .left 87 | // } 88 | // 89 | // messageSenderNameLabel.frame = nameLabelRect 90 | // messageSenderNameLabel.text = message.messageSender.senderName 91 | // messageSenderNameLabel.textAlignment = nameLabelTextAlignment 92 | // self.addSubview(messageSenderNameLabel) 93 | // } 94 | 95 | //MARK: - addSendStatusView 96 | func addSendStatusView(_ bubbleFrame: CGRect) { 97 | let statusViewRect = CGRect(x: bubbleFrame.origin.x - FTDefaultMessageCellSendStatusViewSize - FTDefaultMargin, y: (bubbleFrame.origin.y + bubbleFrame.size.height - FTDefaultMessageCellSendStatusViewSize)/2, width: FTDefaultMessageCellSendStatusViewSize, height: FTDefaultMessageCellSendStatusViewSize) 98 | messageDeliverStatusView?.frame = statusViewRect 99 | messageDeliverStatusView?.setupWithDeliverStatus(message.messageDeliverStatus) 100 | self.addSubview(messageDeliverStatusView!) 101 | } 102 | 103 | @objc func itemTapped() { 104 | print("message item tapped"); 105 | } 106 | } 107 | 108 | 109 | //MARK: - FTChatMessageCell extension 110 | 111 | extension FTChatMessageCell { 112 | 113 | internal class func getCellHeightWithMessage(_ theMessage : FTChatMessageModel, for indexPath: IndexPath) -> CGFloat{ 114 | var cellDesiredHeight : CGFloat = 0; 115 | cellDesiredHeight += FTChatMessageBubbleItem.getMessageBubbleHeightForMessage(theMessage) 116 | cellDesiredHeight += FTDefaultMargin 117 | cellDesiredHeight = max(1, cellDesiredHeight) 118 | return cellDesiredHeight 119 | } 120 | } 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/FTChatMessageDeliverStatusView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageDeliverStatusView.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/9/12. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageDeliverStatusView: UIButton { 12 | 13 | //MARK: - activityIndicator 14 | lazy var activityIndicator : UIActivityIndicatorView = { 15 | let activity = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray) 16 | activity.frame = self.bounds 17 | activity.hidesWhenStopped = true 18 | return activity 19 | }() 20 | 21 | //MARK: - setupWithDeliverStatus 22 | func setupWithDeliverStatus(_ status : FTChatMessageDeliverStatus) { 23 | self.backgroundColor = UIColor.clear 24 | self.addSubview(activityIndicator) 25 | switch status { 26 | case .sending: 27 | activityIndicator.startAnimating() 28 | self.setBackgroundImage(nil, for: UIControlState()) 29 | case .succeeded: 30 | activityIndicator.stopAnimating() 31 | self.setBackgroundImage(nil, for: UIControlState()) 32 | case .failed: 33 | activityIndicator.stopAnimating() 34 | self.setBackgroundImage(UIImage(named: "FT_Error"), for: UIControlState()) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageConversation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageConversation.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2017/1/12. 6 | // Copyright © 2017年 LiuFengting. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageConversation: NSObject { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageDataSource.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/8/19. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol FTChatMessageDataSource : NSObjectProtocol { 12 | 13 | func ftChatMessageMessageArray() -> [FTChatMessageModel] 14 | 15 | } 16 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageDelegate.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/8/19. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol FTChatMessageDelegate : NSObjectProtocol { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageExtensions.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 2017/1/12. 6 | // Copyright © 2017年 LiuFengting. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageExtensions: NSObject { 12 | 13 | } 14 | 15 | extension UIView { 16 | 17 | public var width: CGFloat { 18 | return self.frame.size.width 19 | } 20 | public var height: CGFloat { 21 | return self.frame.size.height 22 | } 23 | 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageHeader/FTChatMessageHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageHeader.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol FTChatMessageHeaderDelegate { 12 | func ft_chatMessageHeaderDidTappedOnIcon(_ messageSenderModel : FTChatMessageUserModel) 13 | } 14 | 15 | class FTChatMessageHeader: UILabel { 16 | 17 | var iconButton : UIButton! 18 | var messageSenderModel : FTChatMessageUserModel! 19 | var headerViewDelegate : FTChatMessageHeaderDelegate? 20 | 21 | lazy var messageSenderNameLabel: UILabel! = { 22 | let label = UILabel(frame: CGRect.zero) 23 | label.font = FTDefaultTimeLabelFont 24 | label.textAlignment = .center 25 | label.textColor = UIColor.lightGray 26 | return label 27 | }() 28 | 29 | 30 | 31 | convenience init(frame: CGRect, senderModel: FTChatMessageUserModel ) { 32 | self.init(frame : frame) 33 | 34 | 35 | messageSenderModel = senderModel; 36 | self.setupHeader( senderModel, isSender: senderModel.isUserSelf) 37 | } 38 | 39 | 40 | 41 | fileprivate func setupHeader(_ user : FTChatMessageUserModel, isSender: Bool){ 42 | self.backgroundColor = UIColor.clear 43 | 44 | let iconRect = isSender ? CGRect(x: self.frame.width-FTDefaultMargin-FTDefaultIconSize, y: FTDefaultMargin, width: FTDefaultIconSize, height: FTDefaultIconSize) : CGRect(x: FTDefaultMargin, y: FTDefaultMargin, width: FTDefaultIconSize, height: FTDefaultIconSize) 45 | iconButton = UIButton(frame: iconRect) 46 | iconButton.backgroundColor = isSender ? FTDefaultOutgoingColor : FTDefaultIncomingColor 47 | iconButton.layer.cornerRadius = FTDefaultIconSize/2; 48 | iconButton.clipsToBounds = true 49 | iconButton.isUserInteractionEnabled = true 50 | iconButton.addTarget(self, action: #selector(self.iconTapped), for: UIControlEvents.touchUpInside) 51 | self.addSubview(iconButton) 52 | 53 | if (user.senderIconUrl != nil) { 54 | // iconButton.kf.setBackgroundImage(with: URL(string : user.senderIconUrl!), for: .normal, placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil) 55 | } 56 | 57 | 58 | // var nameLabelRect = CGRect( x: 0, y: 0 , width: FTScreenWidth - (FTDefaultMargin*2 + FTDefaultIconSize), height: FTDefaultSectionHeight) 59 | // var nameLabelTextAlignment : NSTextAlignment = .right 60 | // 61 | // if isSender == false { 62 | // nameLabelRect.origin.x = FTDefaultMargin + FTDefaultIconSize + FTDefaultMargin 63 | // nameLabelTextAlignment = .left 64 | // } 65 | // 66 | // messageSenderNameLabel.frame = nameLabelRect 67 | // messageSenderNameLabel.text = user.senderName 68 | // messageSenderNameLabel.textAlignment = nameLabelTextAlignment 69 | // self.addSubview(messageSenderNameLabel) 70 | 71 | 72 | 73 | } 74 | 75 | 76 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 77 | if self.iconButton.frame.contains(point) { 78 | return self.iconButton 79 | } 80 | return nil 81 | } 82 | 83 | 84 | 85 | @objc func iconTapped() { 86 | if (headerViewDelegate != nil) { 87 | headerViewDelegate?.ft_chatMessageHeaderDidTappedOnIcon(messageSenderModel) 88 | } 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageAccessoryItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageAccessoryItem.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/9/22. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //MARK: - FTChatMessageAccessoryItem - 12 | 13 | class FTChatMessageAccessoryItem : UIButton { 14 | 15 | @IBOutlet weak var accessoryImageView: UIImageView! 16 | @IBOutlet weak var accessoryNameLabel: UILabel! 17 | 18 | func setupWithAccessoryViewModel(_ viewModel : FTChatMessageAccessoryViewModel, index : NSInteger) { 19 | 20 | self.tag = index 21 | self.accessoryImageView.image = viewModel.iconImage 22 | self.accessoryNameLabel.text = viewModel.title 23 | } 24 | 25 | } 26 | 27 | //MARK: - FTChatMessageAccessoryViewModel - 28 | 29 | class FTChatMessageAccessoryViewModel: NSObject { 30 | var title : String = "" 31 | var iconImage : UIImage? = nil 32 | 33 | convenience init(title: String, iconImage: UIImage?) { 34 | self.init() 35 | self.title = title 36 | self.iconImage = iconImage 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageAccessoryItem.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageAccessoryView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageAccessoryView.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/4/21. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //MARK: - FTChatMessageAccessoryViewDataSource - 12 | 13 | @objc protocol FTChatMessageAccessoryViewDataSource : NSObjectProtocol { 14 | 15 | func ftChatMessageAccessoryViewModelArray() -> [FTChatMessageAccessoryViewModel] 16 | } 17 | 18 | //MARK: - FTChatMessageAccessoryViewDelegate - 19 | 20 | @objc protocol FTChatMessageAccessoryViewDelegate : NSObjectProtocol { 21 | 22 | func ftChatMessageAccessoryViewDidTappedOnItemAtIndex(_ index : NSInteger) 23 | 24 | } 25 | 26 | //MARK: - FTChatMessageAccessoryView - 27 | 28 | class FTChatMessageAccessoryView: UIView, UIScrollViewDelegate{ 29 | 30 | @IBOutlet weak var scrollView: UIScrollView! 31 | @IBOutlet weak var pageControl: UIPageControl! 32 | var accessoryDataSource : FTChatMessageAccessoryViewDataSource! 33 | var accessoryDelegate : FTChatMessageAccessoryViewDelegate! 34 | 35 | //MARK: - awakeFromNib 36 | override func awakeFromNib() { 37 | super.awakeFromNib() 38 | scrollView.scrollsToTop = false 39 | scrollView.delegate = self 40 | } 41 | 42 | //MARK: - setupWithDataSource 43 | func setupWithDataSource(_ accessoryViewDataSource : FTChatMessageAccessoryViewDataSource , accessoryViewDelegate : FTChatMessageAccessoryViewDelegate) { 44 | 45 | self.setNeedsLayout() 46 | 47 | accessoryDataSource = accessoryViewDataSource 48 | accessoryDelegate = accessoryViewDelegate 49 | 50 | if self.accessoryDelegate == nil || self.accessoryDataSource == nil { 51 | NSException(name: NSExceptionName(rawValue: "Notice"), reason: "FTChatMessageAccessoryView. Missing accessoryDelegate or accessoryDelegate", userInfo: nil).raise() 52 | return 53 | }else{ 54 | DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(Int64(Double(0.1) * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { 55 | self.setupAccessoryView() 56 | } 57 | } 58 | } 59 | 60 | //MARK: - setupAccessoryView 61 | func setupAccessoryView() { 62 | 63 | self.layoutIfNeeded() 64 | 65 | for items in self.scrollView.subviews { 66 | if items.isKind(of: FTChatMessageAccessoryItem.classForCoder()) { 67 | items.removeFromSuperview() 68 | } 69 | } 70 | 71 | 72 | let modelArray = accessoryDataSource.ftChatMessageAccessoryViewModelArray() 73 | 74 | let totalCount = modelArray.count 75 | let totalPage = NSInteger(ceilf(Float(totalCount) / 8)) 76 | self.pageControl.numberOfPages = totalPage 77 | self.scrollView.contentSize = CGSize(width: self.bounds.width * CGFloat(totalPage), height: self.bounds.height) 78 | 79 | let xMargin : CGFloat = (self.bounds.width - FTDefaultAccessoryViewLeftMargin*2 - FTDefaultAccessoryViewItemWidth*4)/3 80 | let yMargin : CGFloat = (self.bounds.height - FTDefaultAccessoryViewTopMargin - FTDefaultAccessoryViewBottomMargin - FTDefaultAccessoryViewItemHeight*2) 81 | 82 | for i in 0...totalCount-1 { 83 | let currentPage = i / 8 84 | let indexForCurrentPage = i % 8 85 | 86 | let x = self.bounds.width * CGFloat(currentPage) + FTDefaultAccessoryViewLeftMargin + (xMargin + FTDefaultAccessoryViewItemWidth)*CGFloat(i % 4) 87 | let y = FTDefaultAccessoryViewTopMargin + (yMargin + FTDefaultAccessoryViewItemHeight) * CGFloat(indexForCurrentPage / 4) 88 | 89 | let item : FTChatMessageAccessoryItem = Bundle.main.loadNibNamed("FTChatMessageAccessoryItem", owner: nil, options: nil)![0] as! FTChatMessageAccessoryItem 90 | item.frame = CGRect(x: x, y: y, width: FTDefaultAccessoryViewItemWidth, height: FTDefaultAccessoryViewItemHeight) 91 | 92 | let model = modelArray[i] 93 | 94 | item.setupWithAccessoryViewModel(model, index: i) 95 | item.addTarget(self, action: #selector(self.buttonItemTapped(_:)), for: UIControlEvents.touchUpInside) 96 | self.scrollView.addSubview(item) 97 | } 98 | } 99 | 100 | //MARK: - scrollViewDidEndDecelerating 101 | func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { 102 | let currentPage = lroundf(Float(scrollView.contentOffset.x/self.bounds.width)) 103 | self.pageControl.currentPage = currentPage 104 | } 105 | 106 | 107 | //MARK: - buttonItemTapped 108 | @objc func buttonItemTapped(_ sender : UIButton) { 109 | if (accessoryDelegate != nil) { 110 | accessoryDelegate.ftChatMessageAccessoryViewDidTappedOnItemAtIndex(sender.tag) 111 | } 112 | } 113 | 114 | } 115 | 116 | 117 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageAccessoryView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageInputView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageInputView.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/3/22. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | enum FTChatMessageInputMode { 12 | case keyboard 13 | case record 14 | case accessory 15 | case none 16 | } 17 | 18 | protocol FTChatMessageInputViewDelegate { 19 | func ft_chatMessageInputViewShouldBeginEditing() 20 | func ft_chatMessageInputViewShouldEndEditing() 21 | func ft_chatMessageInputViewShouldUpdateHeight(_ desiredHeight : CGFloat) 22 | func ft_chatMessageInputViewShouldDoneWithText(_ textString : String) 23 | func ft_chatMessageInputViewShouldShowRecordView() 24 | func ft_chatMessageInputViewShouldShowAccessoryView() 25 | } 26 | 27 | class FTChatMessageInputView: UIToolbar, UITextViewDelegate{ 28 | 29 | var inputDelegate : FTChatMessageInputViewDelegate? 30 | 31 | @IBOutlet weak var recordButton: UIButton! 32 | @IBOutlet weak var inputTextView: UITextView! 33 | @IBOutlet weak var accessoryButton: UIButton! 34 | @IBOutlet weak var inputTextViewTopMargin: NSLayoutConstraint! { 35 | didSet { 36 | self.layoutIfNeeded() 37 | } 38 | } 39 | @IBOutlet weak var inputTextViewBottomMargin: NSLayoutConstraint! { 40 | didSet { 41 | self.layoutIfNeeded() 42 | } 43 | } 44 | 45 | 46 | //MARK: - awakeFromNib - 47 | override func awakeFromNib() { 48 | super.awakeFromNib() 49 | 50 | inputTextView.layer.cornerRadius = FTDefaultInputViewTextCornerRadius 51 | inputTextView.layer.borderColor = UIColor.lightGray.cgColor 52 | inputTextView.layer.borderWidth = 0.5 53 | inputTextView.textContainerInset = FTDefaultInputTextViewEdgeInset 54 | inputTextView.delegate = self 55 | 56 | self.bringSubview(toFront: self.inputTextView); 57 | self.bringSubview(toFront: self.recordButton); 58 | self.bringSubview(toFront: self.accessoryButton); 59 | } 60 | 61 | //MARK: - layoutSubviews - 62 | override func layoutSubviews() { 63 | super.layoutSubviews() 64 | 65 | if self.inputTextView.attributedText.length > 0 { 66 | self.inputTextView.scrollRangeToVisible(NSMakeRange(self.inputTextView.attributedText.length - 1, 1)) 67 | } 68 | } 69 | 70 | //MARK: - recordButtonTapped - 71 | @IBAction func recordButtonTapped(_ sender: UIButton) { 72 | if (inputDelegate != nil) { 73 | inputDelegate!.ft_chatMessageInputViewShouldShowRecordView() 74 | } 75 | } 76 | //MARK: - accessoryButtonTapped - 77 | @IBAction func accessoryButtonTapped(_ sender: UIButton) { 78 | if (inputDelegate != nil) { 79 | inputDelegate!.ft_chatMessageInputViewShouldShowAccessoryView() 80 | } 81 | } 82 | 83 | //MARK: - clearText - 84 | func clearText(){ 85 | inputTextView.text = "" 86 | if (inputDelegate != nil) { 87 | inputDelegate!.ft_chatMessageInputViewShouldUpdateHeight(FTDefaultInputViewHeight) 88 | } 89 | } 90 | 91 | //MARK: - UITextViewDelegate - 92 | func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { 93 | if (inputDelegate != nil) { 94 | inputDelegate!.ft_chatMessageInputViewShouldBeginEditing() 95 | } 96 | return true 97 | } 98 | func textViewShouldEndEditing(_ textView: UITextView) -> Bool { 99 | if (inputDelegate != nil) { 100 | inputDelegate!.ft_chatMessageInputViewShouldEndEditing() 101 | } 102 | return true 103 | } 104 | 105 | func textViewDidChange(_ textView: UITextView) { 106 | if let text : NSAttributedString = textView.attributedText { 107 | let textRect = text.boundingRect(with: CGSize(width: textView.bounds.width - textView.textContainerInset.left - textView.textContainerInset.right, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin , .usesFontLeading, .truncatesLastVisibleLine], context: nil); 108 | 109 | if (inputDelegate != nil) { 110 | inputDelegate!.ft_chatMessageInputViewShouldUpdateHeight(min(max(textRect.height + inputTextViewTopMargin.constant + inputTextViewBottomMargin.constant + textView.textContainerInset.top + textView.textContainerInset.bottom, FTDefaultInputViewHeight), FTDefaultInputViewMaxHeight)) 111 | } 112 | } 113 | } 114 | 115 | func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { 116 | if (text == "\n") { 117 | if (textView.text as NSString).length > 0 { 118 | if (inputDelegate != nil) { 119 | inputDelegate!.ft_chatMessageInputViewShouldDoneWithText(textView.text) 120 | self.clearText() 121 | } 122 | } 123 | return false 124 | } 125 | return true 126 | } 127 | 128 | 129 | } 130 | 131 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageInputView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 26 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageRecorderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageRecorderView.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/4/20. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol FTChatMessageRecorderViewDelegate : NSObjectProtocol { 12 | 13 | func ft_chatMessageRecordViewDidStartRecording() 14 | func ft_chatMessageRecordViewDidCancelRecording() 15 | func ft_chatMessageRecordViewDidStopRecording(_ duriation: TimeInterval, file: Data?) 16 | 17 | } 18 | 19 | 20 | class FTChatMessageRecorderView: UIView { 21 | 22 | @IBOutlet weak var recordButton: UIButton! 23 | @IBOutlet weak var descriptionLabel: UILabel! 24 | var recorderTimer : Timer! 25 | var recorderDelegate : FTChatMessageRecorderViewDelegate! 26 | 27 | 28 | @IBAction func buttonTouchDown(_ sender: UIButton) { 29 | self.startRecording() 30 | if recorderDelegate != nil { 31 | recorderDelegate.ft_chatMessageRecordViewDidStartRecording() 32 | } 33 | } 34 | 35 | @IBAction func buttonTouchUpInside(_ sender: UIButton) { 36 | if recorderDelegate != nil { 37 | recorderDelegate.ft_chatMessageRecordViewDidStopRecording(10, file: nil) 38 | } 39 | } 40 | 41 | @IBAction func buttonTouchUpOutside(_ sender: UIButton) { 42 | if recorderDelegate != nil { 43 | recorderDelegate.ft_chatMessageRecordViewDidCancelRecording() 44 | } 45 | } 46 | 47 | 48 | @IBAction func buttonTouchCancel(_ sender: UIButton) { 49 | 50 | if recorderDelegate != nil { 51 | recorderDelegate.ft_chatMessageRecordViewDidCancelRecording() 52 | } 53 | } 54 | 55 | func startRecording() { 56 | 57 | } 58 | func stopRecording() { 59 | 60 | 61 | 62 | } 63 | 64 | 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageInput/FTChatMessageRecorderView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageMarcos.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageMarcos.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /** 12 | FTChatMessageType 13 | 14 | - Text: Text message 15 | - Image: Image message 16 | - Audio: Audio message 17 | - Video: Video message 18 | - Location: Location message 19 | */ 20 | enum FTChatMessageType { 21 | case text 22 | case image 23 | case audio 24 | case video 25 | case location 26 | case Share 27 | case More 28 | } 29 | 30 | public var FTScreenWidth : CGFloat { return UIScreen.main.bounds.size.width } 31 | public var FTScreenHeight : CGFloat { return UIScreen.main.bounds.size.height } 32 | 33 | 34 | let FTDefaultMargin : CGFloat = 5.0 35 | //MARK: - TimeLabel - 36 | let FTDefaultTimeLabelHeight : CGFloat = 10.0 37 | let FTDefaultTimeLabelFont : UIFont = UIFont.systemFont(ofSize: 10) 38 | //MARK: - NameLabel - 39 | let FTDefaultNameLabelHeight : CGFloat = 20.0 40 | let FTDefaultNameLabelFont : UIFont = UIFont.systemFont(ofSize: 12) 41 | //MARK: - Input - 42 | let FTDefaultInputViewHeight : CGFloat = 44.0 43 | let FTDefaultInputViewMargin : CGFloat = 8.0 44 | let FTDefaultInputViewTextCornerRadius : CGFloat = 8.0 45 | let FTDefaultInputViewMaxHeight : CGFloat = 150.0 46 | let FTDefaultInputTextViewEdgeInset: UIEdgeInsets = UIEdgeInsetsMake(5, 5, 5, 5) 47 | let FTDefaultInputViewBackgroundColor : UIColor = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1) 48 | //MARK: - Accessory - 49 | let FTDefaultAccessoryViewHeight : CGFloat = 216.0 50 | let FTDefaultAccessoryViewItemWidth : CGFloat = 60.0 51 | let FTDefaultAccessoryViewItemHeight : CGFloat = 80.0 52 | let FTDefaultAccessoryViewTopMargin : CGFloat = 15.0 53 | let FTDefaultAccessoryViewLeftMargin : CGFloat = 25.0 54 | let FTDefaultAccessoryViewBottomMargin : CGFloat = 30.0 55 | //MARK: - MessageBubble - 56 | let FTDefaultMessageBubbleTextInViewMaxWidth : CGFloat = FTScreenWidth*0.65 57 | let FTDefaultMessageBubbleAngleWidth : CGFloat = 6.0 58 | let FTDefaultMessageBubbleImageWidth : CGFloat = FTScreenWidth*0.48 59 | let FTDefaultMessageBubbleImageHeight : CGFloat = FTDefaultMessageBubbleImageWidth*0.62 60 | let FTDefaultMessageBubbleMapViewWidth : CGFloat = FTDefaultMessageBubbleImageWidth 61 | let FTDefaultMessageBubbleMapViewHeight : CGFloat = FTDefaultMessageBubbleImageHeight 62 | let FTDefaultMessageBubbleAudioHeight : CGFloat = 36.0 63 | let FTDefaultMessageBubbleAudioIconHeight : CGFloat = 24.0 64 | let FTDefaultMessageBubbleMediaIconHeight : CGFloat = 30.0 65 | //MARK: - MessageCell - 66 | let FTDefaultMessageCellTimeLabelHeight : CGFloat = 20.0 67 | let FTDefaultMessageCellSendStatusViewSize : CGFloat = 20.0 68 | let FTDefaultMessageCellIconToMessageMargin : CGFloat = 2.0 69 | let FTDefaultMessageCellReuseIndentifier = "FTDefaultMessageCellReuseIndentifier" 70 | 71 | 72 | let FTDefaultTextTopMargin : CGFloat = 10.0 73 | let FTDefaultTextLeftMargin : CGFloat = 15.0 74 | let FTDefaultLineSpacing : CGFloat = 2.0 75 | let FTDefaultSectionHeight : CGFloat = 40.0 76 | let FTDefaultIconSize : CGFloat = 30.0 77 | let FTDefaultMessageRoundCorner : CGFloat = 16.0 78 | let FTDefaultFontSize : UIFont = UIFont.systemFont(ofSize: 14) 79 | let FTDefaultOutgoingColor : UIColor = UIColor(red: 1/255, green: 123/255, blue: 225/255, alpha: 1) 80 | let FTDefaultIncomingColor : UIColor = UIColor(red: 229/255, green: 229/255, blue: 234/255, alpha: 1) 81 | //MARK: - Animation - 82 | let FTDefaultMessageDefaultAnimationDuration : Double = 0.3 83 | 84 | 85 | 86 | class FTChatMessagePublicMethods { 87 | 88 | class func getFTDefaultMessageParagraphStyle() -> NSMutableParagraphStyle{ 89 | let paragraphStyle : NSMutableParagraphStyle = NSMutableParagraphStyle() 90 | paragraphStyle.lineSpacing = FTDefaultLineSpacing 91 | return paragraphStyle; 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageModel.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | // MARK: - FTChatMessageDeliverStatus 12 | 13 | enum FTChatMessageDeliverStatus { 14 | case sending 15 | case succeeded 16 | case failed 17 | } 18 | 19 | // MARK: - FTChatMessageModel 20 | 21 | class FTChatMessageModel: NSObject { 22 | 23 | var targetId : String! 24 | var isUserSelf : Bool = false; 25 | var messageText : String! 26 | var messageTimeStamp : String! 27 | var messageType : FTChatMessageType = .text 28 | var messageSender : FTChatMessageUserModel! 29 | var messageExtraData : NSDictionary? 30 | var messageDeliverStatus : FTChatMessageDeliverStatus = FTChatMessageDeliverStatus.succeeded 31 | 32 | // MARK: - convenience init 33 | 34 | convenience init(data : String? ,time : String?, from : FTChatMessageUserModel, type : FTChatMessageType){ 35 | self.init() 36 | self.transformMessage(data,time : time,extraDic: nil,from: from,type: type) 37 | } 38 | 39 | convenience init(data : String?,time : String?, extraDic : NSDictionary?, from : FTChatMessageUserModel, type : FTChatMessageType){ 40 | self.init() 41 | self.transformMessage(data,time : time,extraDic: extraDic,from: from,type: type) 42 | } 43 | 44 | // MARK: - transformMessage 45 | 46 | fileprivate func transformMessage(_ data : String? ,time : String?, extraDic : NSDictionary?, from : FTChatMessageUserModel, type : FTChatMessageType){ 47 | messageType = type 48 | messageText = data 49 | messageTimeStamp = time 50 | messageSender = from 51 | isUserSelf = from.isUserSelf 52 | if (extraDic != nil) { 53 | messageExtraData = extraDic; 54 | } 55 | } 56 | 57 | } 58 | 59 | class FTChatMessageImageModel: FTChatMessageModel { 60 | 61 | var image : UIImage! 62 | var imageUrl : String! 63 | 64 | } 65 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageTableViewController.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/2/28. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageTableViewController: UIViewController, UITableViewDelegate,UITableViewDataSource, FTChatMessageInputViewDelegate, FTChatMessageHeaderDelegate { 12 | 13 | var chatMessageDataArray : [FTChatMessageModel] = [] { 14 | didSet { 15 | self.origanizeAndReload() 16 | } 17 | } 18 | open var messageArray : [[FTChatMessageModel]] = [] 19 | var delegete : FTChatMessageDelegate? 20 | var dataSource : FTChatMessageDataSource? 21 | var messageInputMode : FTChatMessageInputMode = FTChatMessageInputMode.none 22 | 23 | let sender2 = FTChatMessageUserModel.init(id: "2", name: "LiuFengting", icon_url: "http://ww3.sinaimg.cn/mw600/9d319f9agw1f3k8e4pixfj20u00u0ac6.jpg", extra_data: nil, isSelf: true) 24 | 25 | 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | 30 | self.view.addSubview(messageTableView) 31 | 32 | self.view.addSubview(messageInputView) 33 | 34 | self.view.addSubview(messageRecordView) 35 | 36 | self.view.addSubview(messageAccessoryView) 37 | 38 | DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + Double(Int64(0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { 39 | self.scrollToBottom(false) 40 | } 41 | } 42 | 43 | 44 | 45 | 46 | 47 | lazy var messageTableView : UITableView! = { 48 | let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: FTScreenWidth, height: FTScreenHeight), style: .plain) 49 | tableView.separatorStyle = .none 50 | tableView.allowsSelection = false 51 | tableView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.onDrag 52 | tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, FTDefaultInputViewHeight, 0) 53 | tableView.delegate = self 54 | tableView.dataSource = self 55 | 56 | let footer = UIView(frame: CGRect( x: 0, y: 0, width: FTScreenWidth, height: FTDefaultInputViewHeight)) 57 | tableView.tableFooterView = footer 58 | 59 | return tableView 60 | }() 61 | 62 | lazy var messageInputView : FTChatMessageInputView! = { 63 | let inputView : FTChatMessageInputView! = Bundle.main.loadNibNamed("FTChatMessageInputView", owner: nil, options: nil)?[0] as! FTChatMessageInputView 64 | inputView.frame = CGRect(x: 0, y: FTScreenHeight-FTDefaultInputViewHeight, width: FTScreenWidth, height: FTDefaultInputViewHeight) 65 | inputView.inputDelegate = self 66 | return inputView 67 | }() 68 | 69 | lazy var messageRecordView : FTChatMessageRecorderView! = { 70 | let recordView : FTChatMessageRecorderView! = Bundle.main.loadNibNamed("FTChatMessageRecorderView", owner: nil, options: nil)?[0] as! FTChatMessageRecorderView 71 | recordView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 72 | return recordView 73 | }() 74 | 75 | lazy var messageAccessoryView : FTChatMessageAccessoryView! = { 76 | let accessoryView = Bundle.main.loadNibNamed("FTChatMessageAccessoryView", owner: nil, options: nil)?[0] as! FTChatMessageAccessoryView 77 | accessoryView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 78 | return accessoryView 79 | }() 80 | 81 | 82 | 83 | 84 | func repositionEverything() { 85 | self.messageTableView.frame = CGRect(x: 0, y: 0, width: FTScreenWidth, height: FTScreenHeight) 86 | self.messageInputView.frame = CGRect(x: 0, y: FTScreenHeight-FTDefaultInputViewHeight, width: FTScreenWidth, height: FTDefaultInputViewHeight) 87 | self.messageRecordView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 88 | self.messageAccessoryView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 89 | self.messageTableView.reloadData() 90 | } 91 | 92 | 93 | 94 | 95 | 96 | 97 | internal func addNewMessage(_ message : FTChatMessageModel) { 98 | 99 | chatMessageDataArray.append(message); 100 | 101 | self.origanizeAndReload() 102 | 103 | self.scrollToBottom(true) 104 | 105 | } 106 | 107 | func origanizeAndReload() { 108 | var nastyArray : [[FTChatMessageModel]] = [] 109 | var tempArray : [FTChatMessageModel] = [] 110 | var tempId = "" 111 | for i in 0...chatMessageDataArray.count-1 { 112 | let message = chatMessageDataArray[i] 113 | if message.messageSender.senderId == tempId { 114 | tempArray.append(message) 115 | }else{ 116 | tempId = message.messageSender.senderId; 117 | if tempArray.count > 0 { 118 | nastyArray.append(tempArray) 119 | } 120 | tempArray.removeAll() 121 | tempArray.append(message) 122 | } 123 | if i == chatMessageDataArray.count - 1 { 124 | if tempArray.count > 0 { 125 | nastyArray.append(tempArray) 126 | } 127 | } 128 | } 129 | 130 | self.messageArray = nastyArray 131 | self.messageTableView.reloadData() 132 | } 133 | 134 | 135 | 136 | 137 | } 138 | -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageUserModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FTChatMessageUserModel.swift 3 | // FTChatMessage 4 | // 5 | // Created by liufengting on 16/8/21. 6 | // Copyright © 2016年 liufengting . All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class FTChatMessageUserModel : NSObject{ 12 | 13 | var isUserSelf : Bool = false; 14 | var senderId : String! 15 | var senderName : String! 16 | var senderIconUrl : String! 17 | var senderExtraData : NSDictionary? 18 | 19 | // MARK: - convenience init 20 | convenience init(id : String? ,name : String?, icon_url : String?, extra_data : NSDictionary?, isSelf : Bool){ 21 | self.init() 22 | senderId = id 23 | senderName = name 24 | senderIconUrl = icon_url 25 | senderExtraData = extra_data 26 | isUserSelf = isSelf 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Liu Fengting 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![FTChatMessage](https://raw.githubusercontent.com/liufengting/FTResourceRepo/master/Resource/FTChatMessage/FTChatMessage.jpg) 2 | 3 | # FTChatMessage 4 | 5 | [![Twitter](https://img.shields.io/badge/twitter-@liufengting-blue.svg?style=flat)](http://twitter.com/liufengting) 6 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/liufengting/FTChatMessage/master/LICENSE) 7 | [![GitHub stars](https://img.shields.io/github/stars/liufengting/FTChatMessage.svg)](https://github.com/liufengting/FTChatMessage/stargazers) 8 | 9 | 10 | # Working in progress 11 | 12 | Still working in progress. 13 | 14 | # Requirements 15 | 16 | Swift 3.0 Xcode 8.0 17 | 18 | # CHANGELOG 19 | 20 | [See CHANGELOG](https://github.com/liufengting/FTChatMessage/blob/master/CHANGELOG.md) 21 | 22 | # ScreenShots 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | # Preview 34 | 35 |
36 | 37 | 38 | # Something 39 | 40 | 24 Dec, 2016. It's Christmas tomorrow. It's hard for me to focus on something recently. I'm thinking of rewrite it from the ground up. I have been write some other frameworks that I'm interested in, mostly not perfect. So sometimes I feel so depressed. 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ResourceImages/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/ResourceImages/1.jpg -------------------------------------------------------------------------------- /ResourceImages/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/ResourceImages/2.jpg -------------------------------------------------------------------------------- /ResourceImages/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/ResourceImages/3.jpg -------------------------------------------------------------------------------- /ResourceImages/ChatMessageDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessage/4eab5515d975681735ac7df1f19d0af259dc7b61/ResourceImages/ChatMessageDemo.gif --------------------------------------------------------------------------------