├── .DS_Store ├── .gitignore ├── CHANGELOG.md ├── Demo ├── .DS_Store ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Demo.xcworkspace │ └── contents.xcworkspacedata ├── Demo │ ├── .DS_Store │ ├── 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 │ │ ├── Image.swift │ │ ├── ImageCache.swift │ │ ├── ImageDownloader.swift │ │ ├── ImagePrefetcher.swift │ │ ├── ImageProcessor.swift │ │ ├── ImageTransition.swift │ │ ├── ImageView+Kingfisher.swift │ │ ├── Indicator.swift │ │ ├── Kingfisher.h │ │ ├── Kingfisher.swift │ │ ├── KingfisherManager.swift │ │ ├── KingfisherOptionsInfo.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 ├── .DS_Store ├── 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 ├── .DS_Store ├── 1.jpg ├── 2.jpg ├── 3.jpg └── ChatMessageDemo.gif /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/.DS_Store -------------------------------------------------------------------------------- /.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 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/screenshots 64 | -------------------------------------------------------------------------------- /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 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Demo/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/Demo/.DS_Store -------------------------------------------------------------------------------- /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/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/Demo/Demo/.DS_Store -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Demo 4 | // 5 | // Created by 刘锋婷 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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/Demo/Demo/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /Demo/Demo/Assets.xcassets/Alarm.imageset/Alarm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/Demo/Demo/Assets.xcassets/Weather.imageset/Weather.png -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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 | 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.showProgressWithmessage("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 | // Demo 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 | // Demo 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 | // Demo 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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/Demo/Demo/dog.jpg -------------------------------------------------------------------------------- /Demo/Demo/pinyin.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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.1.5): 4 | - FTIndicator/FTNotificationIndicator (= 1.1.5) 5 | - FTIndicator/FTProgressIndicator (= 1.1.5) 6 | - FTIndicator/FTToastIndicator (= 1.1.5) 7 | - FTIndicator/FTNotificationIndicator (1.1.5) 8 | - FTIndicator/FTProgressIndicator (1.1.5) 9 | - FTIndicator/FTToastIndicator (1.1.5) 10 | - FTPickerView (1.0.1) 11 | - Kingfisher (3.2.4) 12 | 13 | DEPENDENCIES: 14 | - FTImageSize 15 | - FTIndicator 16 | - FTPickerView 17 | - Kingfisher 18 | 19 | SPEC CHECKSUMS: 20 | FTImageSize: 29c6fc0830dbe33973286148a8e32cd582a9f26d 21 | FTIndicator: 8b9885b84340a19029ad13672add3bb8183feba8 22 | FTPickerView: fd26852f56c352c777d1dafc87ed4c2138eeea4c 23 | Kingfisher: 8d80f39da403cd9c9ee11984e1655f4d6a566cdb 24 | 25 | PODFILE CHECKSUM: 0897bbd78b2258d0c7f01ee2e9d5eb5a802cde17 26 | 27 | COCOAPODS: 1.1.1 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.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTIndicator.h 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 10 | #import "FTToastIndicator.h" 11 | #import "FTProgressIndicator.h" 12 | #import "FTNotificationIndicator.h" 13 | 14 | #pragma mark - FTIndicator 15 | /** 16 | * FTIndicator 17 | */ 18 | @interface FTIndicator : NSObject 19 | 20 | /** 21 | * set Style 22 | */ 23 | #pragma mark - set theme style 24 | /** 25 | * setIndicatorStyleToDefaultStyle 26 | */ 27 | +(void)setIndicatorStyleToDefaultStyle; 28 | /** 29 | * setIndicatorStyle 30 | * 31 | * @param style UIBlurEffectStyle style 32 | */ 33 | +(void)setIndicatorStyle:(UIBlurEffectStyle)style; 34 | 35 | /** 36 | * FTToastIndicator 37 | */ 38 | #pragma mark - FTToastIndicator 39 | 40 | /** 41 | * showToastMessage 42 | * 43 | * @param toastMessage NSString toastMessage 44 | */ 45 | +(void)showToastMessage:(NSString *)toastMessage; 46 | 47 | /** 48 | * dismissToast 49 | */ 50 | +(void)dismissToast; 51 | 52 | /** 53 | * FTProgressIndicator 54 | */ 55 | #pragma mark - FTProgressIndicator 56 | /** 57 | * showProgressWithmessage 58 | * 59 | * @param message message 60 | */ 61 | +(void)showProgressWithmessage:(NSString *)message; 62 | 63 | /** 64 | * showProgressWithmessage userInteractionEnable 65 | * 66 | * @param message message 67 | * @param userInteractionEnable userInteractionEnable 68 | */ 69 | +(void)showProgressWithmessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 70 | 71 | /** 72 | * showInfoWithMessage 73 | * 74 | * @param message NSString message 75 | */ 76 | +(void)showInfoWithMessage:(NSString *)message; 77 | 78 | /** 79 | * showInfoWithMessage userInteractionEnable 80 | * 81 | * @param message message 82 | * @param userInteractionEnable userInteractionEnable 83 | */ 84 | +(void)showInfoWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 85 | 86 | /** 87 | * showSuccessWithMessage 88 | * 89 | * @param message NSString message 90 | */ 91 | +(void)showSuccessWithMessage:(NSString *)message; 92 | 93 | /** 94 | * showSuccessWithMessage userInteractionEnable 95 | * 96 | * @param message message 97 | * @param userInteractionEnable userInteractionEnable 98 | */ 99 | +(void)showSuccessWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 100 | 101 | /** 102 | * showErrorWithMessage 103 | * 104 | * @param message NSString message 105 | */ 106 | +(void)showErrorWithMessage:(NSString *)message; 107 | 108 | /** 109 | * showErrorWithMessage userInteractionEnable 110 | * 111 | * @param message message 112 | * @param userInteractionEnable userInteractionEnable 113 | */ 114 | +(void)showErrorWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 115 | 116 | /** 117 | * dismissProgress 118 | */ 119 | +(void)dismissProgress; 120 | 121 | /** 122 | * FTNotificationIndicator 123 | */ 124 | #pragma mark - FTNotificationIndicator 125 | /** 126 | * showNotificationWithTitle 127 | * 128 | * @param title title 129 | * @param message message 130 | */ 131 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message; 132 | /** 133 | * showNotificationWithTitle message tapHandler 134 | * 135 | * @param title title 136 | * @param message message 137 | * @param tapHandler tapHandler 138 | */ 139 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 140 | /** 141 | * showNotificationWithTitle message tapHandler completion 142 | * 143 | * @param title title 144 | * @param message message 145 | * @param tapHandler tapHandler 146 | */ 147 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 148 | /** 149 | * showNotificationWithImage title message 150 | * 151 | * @param image image 152 | * @param title title 153 | * @param message message 154 | */ 155 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message; 156 | /** 157 | * showNotificationWithImage title message tapHandler 158 | * 159 | * @param image image 160 | * @param title title 161 | * @param message message 162 | * @param tapHandler tapHandler 163 | */ 164 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 165 | /** 166 | * showNotificationWithImage title message tapHandler completion 167 | * 168 | * @param image image 169 | * @param title title 170 | * @param message message 171 | * @param tapHandler tapHandler 172 | */ 173 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 174 | /** 175 | * dismissNotification 176 | */ 177 | +(void)dismissNotification; 178 | 179 | @end 180 | -------------------------------------------------------------------------------- /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 | 30 | 31 | #pragma mark - FTToastIndicator 32 | /** 33 | * FTToastIndicator 34 | */ 35 | +(void)showToastMessage:(NSString *)toastMessage 36 | { 37 | [FTToastIndicator showToastMessage:toastMessage]; 38 | } 39 | 40 | +(void)dismissToast 41 | { 42 | [FTToastIndicator dismiss]; 43 | } 44 | 45 | 46 | #pragma mark - FTProgressIndicator 47 | /** 48 | * FTProgressIndicator 49 | */ 50 | +(void)showProgressWithmessage:(NSString *)message 51 | { 52 | [FTProgressIndicator showProgressWithmessage:message userInteractionEnable:YES]; 53 | } 54 | +(void)showProgressWithmessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 55 | { 56 | [FTProgressIndicator showProgressWithmessage:message userInteractionEnable:userInteractionEnable]; 57 | } 58 | +(void)showInfoWithMessage:(NSString *)message 59 | { 60 | [FTProgressIndicator showInfoWithMessage:message userInteractionEnable:YES]; 61 | } 62 | +(void)showInfoWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 63 | { 64 | [FTProgressIndicator showInfoWithMessage:message userInteractionEnable:userInteractionEnable]; 65 | } 66 | +(void)showSuccessWithMessage:(NSString *)message 67 | { 68 | [FTProgressIndicator showSuccessWithMessage:message userInteractionEnable:YES]; 69 | } 70 | +(void)showSuccessWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 71 | { 72 | [FTProgressIndicator showSuccessWithMessage:message userInteractionEnable:userInteractionEnable]; 73 | } 74 | +(void)showErrorWithMessage:(NSString *)message 75 | { 76 | [FTProgressIndicator showErrorWithMessage:message userInteractionEnable:YES]; 77 | } 78 | +(void)showErrorWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable 79 | { 80 | [FTProgressIndicator showErrorWithMessage:message userInteractionEnable:userInteractionEnable]; 81 | } 82 | +(void)dismissProgress 83 | { 84 | [FTProgressIndicator dismiss]; 85 | } 86 | 87 | #pragma mark - FTNotificationIndicator 88 | /** 89 | * FTNotificationIndicator 90 | */ 91 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message 92 | { 93 | [FTNotificationIndicator showNotificationWithTitle:title message:message]; 94 | } 95 | 96 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler 97 | { 98 | [FTNotificationIndicator showNotificationWithTitle:title message:message tapHandler:tapHandler]; 99 | } 100 | 101 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion 102 | { 103 | [FTNotificationIndicator showNotificationWithTitle:title message:message tapHandler:tapHandler completion:completion]; 104 | } 105 | 106 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message 107 | { 108 | [FTNotificationIndicator showNotificationWithImage:image title:title message:message]; 109 | } 110 | 111 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler 112 | { 113 | [FTNotificationIndicator showNotificationWithImage:image title:title message:message tapHandler:tapHandler]; 114 | } 115 | 116 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion 117 | { 118 | [FTNotificationIndicator showNotificationWithImage:image title:title message:message tapHandler:tapHandler completion:completion]; 119 | } 120 | 121 | +(void)dismissNotification 122 | { 123 | [FTNotificationIndicator dismiss]; 124 | } 125 | 126 | @end 127 | -------------------------------------------------------------------------------- /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 | typedef void (^FTNotificationTapHandler)(void); 13 | typedef void (^FTNotificationCompletion)(void); 14 | 15 | /** 16 | * FTNotificationIndicator 17 | */ 18 | @interface FTNotificationIndicator : NSObject 19 | /** 20 | * setIndicatorStyleToDefaultStyle 21 | */ 22 | +(void)setNotificationIndicatorStyleToDefaultStyle; 23 | /** 24 | * setIndicatorStyle 25 | * 26 | * @param style UIBlurEffectStyle style 27 | */ 28 | +(void)setNotificationIndicatorStyle:(UIBlurEffectStyle)style; 29 | /** 30 | * showNotificationWithTitle message 31 | * 32 | * @param title title 33 | * @param message message 34 | */ 35 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message; 36 | /** 37 | * showNotificationWithTitle message tapHandler 38 | * 39 | * @param title title 40 | * @param message message 41 | * @param tapHandler tapHandler 42 | */ 43 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 44 | /** 45 | * showNotificationWithTitle message tapHandler completion 46 | * 47 | * @param title title 48 | * @param message message 49 | * @param FTTapNotificationHandler tapHandler 50 | * @param FTNotificationCompletion completion 51 | */ 52 | +(void)showNotificationWithTitle:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 53 | /** 54 | * showNotificationWithImage title message 55 | * 56 | * @param image image 57 | * @param title title 58 | * @param message message 59 | */ 60 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message; 61 | /** 62 | * showNotificationWithImage title message tapHandler 63 | * 64 | * @param image image 65 | * @param title title 66 | * @param message message 67 | * @param tapHandler tapHandler 68 | */ 69 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler; 70 | /** 71 | * showNotificationWithImage title message tapHandler completion 72 | * 73 | * @param image image 74 | * @param title title 75 | * @param message message 76 | * @param FTTapNotificationHandler tapHandler 77 | * @param FTNotificationCompletion completion 78 | */ 79 | +(void)showNotificationWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message tapHandler:(FTNotificationTapHandler)tapHandler completion:(FTNotificationCompletion)completion; 80 | /** 81 | * dismiss 82 | */ 83 | +(void)dismiss; 84 | 85 | @end 86 | 87 | #pragma mark - FTNotificationIndicatorView 88 | /** 89 | * FTNotificationIndicatorView 90 | */ 91 | @interface FTNotificationIndicatorView : UIVisualEffectView 92 | /** 93 | * showWithImage 94 | * 95 | * @param image image 96 | * @param title title 97 | * @param message message 98 | * @param style style 99 | */ 100 | -(void)showWithImage:(UIImage *)image title:(NSString *)title message:(NSString *)message style:(UIBlurEffectStyle)style; 101 | /** 102 | * getFrameForNotificationViewWithImage 103 | * 104 | * @param image image 105 | * @param notificationMessage message 106 | * 107 | * @return CGSize 108 | */ 109 | -(CGSize )getFrameForNotificationViewWithImage:(UIImage *)image message:(NSString *)notificationMessage; 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/FTIndicator/FTProgressIndicator/FTProgressIndicator.bundle/ft_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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 | * FTProgressIndicatorMessageType 14 | */ 15 | typedef NS_ENUM(NSUInteger, FTProgressIndicatorMessageType) { 16 | /** 17 | * Info 18 | */ 19 | FTProgressIndicatorMessageTypeInfo, 20 | /** 21 | * Success 22 | */ 23 | FTProgressIndicatorMessageTypeSuccess, 24 | /** 25 | * Error 26 | */ 27 | FTProgressIndicatorMessageTypeError, 28 | /** 29 | * Progress 30 | */ 31 | FTProgressIndicatorMessageTypeProgress 32 | }; 33 | 34 | #pragma mark - FTProgressIndicator 35 | /** 36 | * FTProgressIndicator 37 | */ 38 | @interface FTProgressIndicator : NSObject 39 | 40 | /** 41 | * showProgressWithmessage 42 | * 43 | * @param message message 44 | */ 45 | +(void)showProgressWithmessage:(NSString *)message; 46 | 47 | /** 48 | * showProgressWithmessage userInteractionEnable 49 | * 50 | * @param message message 51 | * @param userInteractionEnable userInteractionEnable 52 | */ 53 | +(void)showProgressWithmessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 54 | 55 | /** 56 | * showInfoWithMessage 57 | * 58 | * @param message NSString message 59 | */ 60 | +(void)showInfoWithMessage:(NSString *)message; 61 | 62 | /** 63 | * showInfoWithMessage userInteractionEnable 64 | * 65 | * @param message message 66 | * @param userInteractionEnable userInteractionEnable 67 | */ 68 | +(void)showInfoWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 69 | 70 | /** 71 | * showSuccessWithMessage 72 | * 73 | * @param message NSString message 74 | */ 75 | +(void)showSuccessWithMessage:(NSString *)message; 76 | 77 | /** 78 | * showSuccessWithMessage userInteractionEnable 79 | * 80 | * @param message message 81 | * @param userInteractionEnable userInteractionEnable 82 | */ 83 | +(void)showSuccessWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 84 | 85 | /** 86 | * showErrorWithMessage 87 | * 88 | * @param message NSString message 89 | */ 90 | +(void)showErrorWithMessage:(NSString *)message; 91 | 92 | /** 93 | * showErrorWithMessage userInteractionEnable 94 | * 95 | * @param message message 96 | * @param userInteractionEnable userInteractionEnable 97 | */ 98 | +(void)showErrorWithMessage:(NSString *)message userInteractionEnable:(BOOL)userInteractionEnable; 99 | 100 | /** 101 | * dismiss 102 | */ 103 | +(void)dismiss; 104 | 105 | /** 106 | * setProgressIndicatorStyleToDefaultStyle 107 | */ 108 | +(void)setProgressIndicatorStyleToDefaultStyle; 109 | 110 | /** 111 | * setProgressIndicatorStyle 112 | * 113 | * @param style UIBlurEffectStyle style 114 | */ 115 | +(void)setProgressIndicatorStyle:(UIBlurEffectStyle)style; 116 | 117 | @end 118 | 119 | #pragma FTProgressIndicatorView 120 | /** 121 | * FTProgressIndicatorView 122 | */ 123 | @interface FTProgressIndicatorView : UIVisualEffectView 124 | /** 125 | * userInteractionEnable, if allows user touches at view 126 | */ 127 | @property (assign, nonatomic) BOOL userInteractionEnable; 128 | /** 129 | * showProgressWithType 130 | * 131 | * @param type type 132 | * @param message message 133 | * @param style style 134 | * @param userInteractionEnable userInteractionEnable 135 | */ 136 | -(void)showProgressWithType:(FTProgressIndicatorMessageType )type message:(NSString *)message style:(UIBlurEffectStyle)style userInteractionEnable:(BOOL)userInteractionEnable; 137 | /** 138 | * getFrameForProgressViewWithMessage 139 | * 140 | * @param progressMessage progressMessage 141 | * 142 | * @return CGSize 143 | */ 144 | -(CGSize )getFrameForProgressViewWithMessage:(NSString *)progressMessage; 145 | 146 | @end -------------------------------------------------------------------------------- /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 UIBlurEffectStyle style 24 | */ 25 | +(void)setToastIndicatorStyle:(UIBlurEffectStyle)style; 26 | /** 27 | * showToastMessage 28 | * 29 | * @param toastMessage NSString toastMessage 30 | */ 31 | +(void)showToastMessage:(NSString *)toastMessage; 32 | /** 33 | * dismiss 34 | */ 35 | +(void)dismiss; 36 | 37 | @end 38 | 39 | #pragma mark - FTToastIndicatorView 40 | /** 41 | * FTToastIndicatorView 42 | */ 43 | @interface FTToastIndicatorView : UIVisualEffectView 44 | /** 45 | * showToastMessage 46 | * 47 | * @param toastMessage toastMessage 48 | * @param style style 49 | */ 50 | -(void)showToastMessage:(NSString *)toastMessage withStyle:(UIBlurEffectStyle)style; 51 | /** 52 | * getFrameForToastViewWithMessage 53 | * 54 | * @param toastMessage toastMessage 55 | * 56 | * @return CGSize 57 | */ 58 | -(CGSize )getFrameForToastViewWithMessage:(NSString *)toastMessage; 59 | 60 | @end -------------------------------------------------------------------------------- /Demo/Pods/FTIndicator/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/FTPickerView/FTPickerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FTPickerView.h 3 | // FTPickerView 4 | // 5 | // Created by liufengting on 15/12/3. 6 | // Copyright © 2015年 liufengting. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void (^ FTPickerDoneBlock )(NSInteger); 12 | typedef void (^ FTPickerCancelBlock )(); 13 | typedef void (^ FTDatePickerDoneBlock )(NSDate *); 14 | typedef void (^ FTDatePickerCancelBlock )(); 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) 2015 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/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Kingfisher 4 | 5 |

6 | 7 |

8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | codebeat badge 22 | 23 | 24 | 25 | 26 |

27 | 28 | Kingfisher is a lightweight, pure-Swift library for downloading and caching images from the web. This project is heavily inspired by the popular [SDWebImage](https://github.com/rs/SDWebImage). It provides you a chance to use a pure-Swift alternative in your next app. 29 | 30 | ## Features 31 | 32 | - [x] Asynchronous image downloading and caching. 33 | - [x] `URLSession`-based networking. Basic image processors and filters supplied. 34 | - [x] Multiple-layer cache for both memory and disk. 35 | - [x] Cancelable downloading and processing tasks to improve performance. 36 | - [x] Independent components. Use the downloader or caching system separately as you need. 37 | - [x] Prefetching images and showing them from cache later when necessary. 38 | - [x] Extensions for `UIImageView`, `NSImage` and `UIButton` to directly set an image from a URL. 39 | - [x] Built-in transition animation when setting images. 40 | - [x] Extensible image processing and image format support. 41 | 42 | The simplest use-case is setting an image to an image view with the `UIImageView` extension: 43 | 44 | ```swift 45 | let url = URL(string: "url_of_your_image") 46 | imageView.kf.setImage(with: url) 47 | ``` 48 | 49 | Kingfisher will download the image from `url`, send it to both the memory cache and the disk cache, and display it in `imageView`. When you use the same code later, the image will be retrieved from cache and shown immediately. 50 | 51 | ## Requirements 52 | 53 | - iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+ 54 | - Swift 3 (Kingfisher 3.x), Swift 2.3 (Kingfisher 2.x) 55 | 56 | Main development of Kingfisher will support Swift 3. Only critical bug fixes will be made for Kingfisher 2.x. 57 | 58 | [Kingfisher 3.0 Migration Guide](https://github.com/onevcat/Kingfisher/wiki/Kingfisher-3.0-Migration-Guide) - If you are upgrading to Kingfisher 3.x from an earlier version, please read this for more information. 59 | 60 | ## Next Steps 61 | 62 | We prepared a [wiki page](https://github.com/onevcat/Kingfisher/wiki). You can find tons of useful things there. 63 | 64 | * [Installation Guide](https://github.com/onevcat/Kingfisher/wiki/Installation-Guide) - Follow it to integrate Kingfisher into your project. 65 | * [Cheat Sheet](https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet)- Curious about what Kingfisher could do and how would it look like when used in your project? See this page for useful code snippets. If you are already familiar with Kingfisher, you could also learn new tricks to improve the way you use Kingfisher! 66 | * [API Reference](http://cocoadocs.org/docsets/Kingfisher/) - Lastly, please remember to read the full whenever you may need a more detailed reference. 67 | 68 | ## Other 69 | 70 | ### Future of Kingfisher 71 | 72 | I want to keep Kingfisher lightweight. This framework will focus on providing a simple solution for downloading and caching images. This doesn’t mean the framework can’t be improved. Kingfisher is far from perfect, so necessary and useful updates will be made to make it better. 73 | 74 | ### About the logo 75 | 76 | The logo of Kingfisher is inspired by [Tangram (七巧板)](http://en.wikipedia.org/wiki/Tangram), a dissection puzzle consisting of seven flat shapes from China. I believe she's a kingfisher bird instead of a swift, but someone insists that she is a pigeon. I guess I should give her a name. Hi, guys, do you have any suggestions? 77 | 78 | ### Contact 79 | 80 | Follow and contact me on [Twitter](http://twitter.com/onevcat) or [Sina Weibo](http://weibo.com/onevcat). If you find an issue, just [open a ticket](https://github.com/onevcat/Kingfisher/issues/new). Pull requests are warmly welcome as well. 81 | 82 | ### License 83 | 84 | Kingfisher is released under the MIT license. See LICENSE for details. 85 | 86 | 87 | -------------------------------------------------------------------------------- /Demo/Pods/Kingfisher/Sources/Box.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Box.swift 3 | // Kingfisher 4 | // 5 | // Created by WANG WEI on 2016/09/12. 6 | // Copyright © 2016年 Wei Wang. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Box { 12 | let value: T 13 | init(value: T) { 14 | self.value = value 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /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) 2016 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 scale = (options ?? KingfisherEmptyOptionsInfo).scaleFactor 81 | let preloadAllGIFData = (options ?? KingfisherEmptyOptionsInfo).preloadAllGIFData 82 | 83 | return Kingfisher.image(data: data, scale: scale, preloadAllGIFData: preloadAllGIFData) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /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) 2016 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 | return filter.outputImage?.cropping(to: input.extent) 75 | } 76 | } 77 | 78 | public typealias ColorElement = (CGFloat, CGFloat, CGFloat, CGFloat) 79 | 80 | /// Color control filter which will apply color control change to images. 81 | public static var colorControl: (ColorElement) -> Filter = { 82 | brightness, contrast, saturation, inputEV in 83 | Filter { input in 84 | let paramsColor = [kCIInputBrightnessKey: brightness, 85 | kCIInputContrastKey: contrast, 86 | kCIInputSaturationKey: saturation] 87 | 88 | let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor) 89 | let paramsExposure = [kCIInputEVKey: inputEV] 90 | return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure) 91 | } 92 | 93 | } 94 | } 95 | 96 | extension Kingfisher where Base: Image { 97 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 98 | /// 99 | /// - parameter filter: The filter used to transform `self`. 100 | /// 101 | /// - returns: A transformed image by input `Filter`. 102 | /// 103 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 104 | public func apply(_ filter: Filter) -> Image { 105 | 106 | guard let cgImage = cgImage else { 107 | assertionFailure("[Kingfisher] Tint image only works for CG-based image.") 108 | return base 109 | } 110 | 111 | let inputImage = CIImage(cgImage: cgImage) 112 | guard let outputImage = filter.transform(inputImage) else { 113 | return base 114 | } 115 | 116 | guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else { 117 | assertionFailure("[Kingfisher] Can not make an tint image within context.") 118 | return base 119 | } 120 | 121 | #if os(macOS) 122 | return fixedForRetinaPixel(cgImage: result, to: size) 123 | #else 124 | return Image(cgImage: result, scale: base.scale, orientation: base.imageOrientation) 125 | #endif 126 | } 127 | 128 | } 129 | 130 | public extension Image { 131 | 132 | /// Apply a `Filter` containing `CIImage` transformer to `self`. 133 | /// 134 | /// - parameter filter: The filter used to transform `self`. 135 | /// 136 | /// - returns: A transformed image by input `Filter`. 137 | /// 138 | /// - Note: Only CG-based images are supported. If any error happens during transforming, `self` will be returned. 139 | @available(*, deprecated, 140 | message: "Extensions directly on Image are deprecated. Use `kf.apply` instead.", 141 | renamed: "kf.apply") 142 | public func kf_apply(_ filter: Filter) -> Image { 143 | return kf.apply(filter) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /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) 2016 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) 2016 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) 2016 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 Color = NSColor 34 | public typealias ImageView = NSImageView 35 | typealias Button = NSButton 36 | #else 37 | import UIKit 38 | public typealias Image = UIImage 39 | public typealias Color = UIColor 40 | #if !os(watchOS) 41 | public typealias ImageView = UIImageView 42 | typealias Button = UIButton 43 | #endif 44 | #endif 45 | 46 | public final class Kingfisher { 47 | public let base: Base 48 | public init(_ base: Base) { 49 | self.base = base 50 | } 51 | } 52 | 53 | /** 54 | A type that has Kingfisher extensions. 55 | */ 56 | public protocol KingfisherCompatible { 57 | associatedtype CompatibleType 58 | var kf: CompatibleType { get } 59 | } 60 | 61 | public extension KingfisherCompatible { 62 | public var kf: Kingfisher { 63 | get { return Kingfisher(self) } 64 | } 65 | } 66 | 67 | extension Image: KingfisherCompatible { } 68 | #if !os(watchOS) 69 | extension ImageView: KingfisherCompatible { } 70 | extension Button: KingfisherCompatible { } 71 | #endif 72 | -------------------------------------------------------------------------------- /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) 2016 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) 2016 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) 2016 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.1.5): 4 | - FTIndicator/FTNotificationIndicator (= 1.1.5) 5 | - FTIndicator/FTProgressIndicator (= 1.1.5) 6 | - FTIndicator/FTToastIndicator (= 1.1.5) 7 | - FTIndicator/FTNotificationIndicator (1.1.5) 8 | - FTIndicator/FTProgressIndicator (1.1.5) 9 | - FTIndicator/FTToastIndicator (1.1.5) 10 | - FTPickerView (1.0.1) 11 | - Kingfisher (3.2.4) 12 | 13 | DEPENDENCIES: 14 | - FTImageSize 15 | - FTIndicator 16 | - FTPickerView 17 | - Kingfisher 18 | 19 | SPEC CHECKSUMS: 20 | FTImageSize: 29c6fc0830dbe33973286148a8e32cd582a9f26d 21 | FTIndicator: 8b9885b84340a19029ad13672add3bb8183feba8 22 | FTPickerView: fd26852f56c352c777d1dafc87ed4c2138eeea4c 23 | Kingfisher: 8d80f39da403cd9c9ee11984e1655f4d6a566cdb 24 | 25 | PODFILE CHECKSUM: 0897bbd78b2258d0c7f01ee2e9d5eb5a802cde17 26 | 27 | COCOAPODS: 1.1.1 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 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTImageSize/FTImageSize-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | 6 | FOUNDATION_EXPORT double FTImageSizeVersionNumber; 7 | FOUNDATION_EXPORT const unsigned char FTImageSizeVersionString[]; 8 | 9 | -------------------------------------------------------------------------------- /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 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | SWIFT_VERSION = 3.0 11 | -------------------------------------------------------------------------------- /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 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTIndicator/FTIndicator-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "FTIndicator.h" 6 | #import "FTNotificationIndicator.h" 7 | #import "FTProgressIndicator.h" 8 | #import "FTToastIndicator.h" 9 | #import "FTNotificationIndicator.h" 10 | #import "FTProgressIndicator.h" 11 | #import "FTToastIndicator.h" 12 | 13 | FOUNDATION_EXPORT double FTIndicatorVersionNumber; 14 | FOUNDATION_EXPORT const unsigned char FTIndicatorVersionString[]; 15 | 16 | -------------------------------------------------------------------------------- /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 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 8 | SKIP_INSTALL = YES 9 | -------------------------------------------------------------------------------- /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.1.5 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 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/FTPickerView/FTPickerView-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "FTPickerView.h" 6 | 7 | FOUNDATION_EXPORT double FTPickerViewVersionNumber; 8 | FOUNDATION_EXPORT const unsigned char FTPickerViewVersionString[]; 9 | 10 | -------------------------------------------------------------------------------- /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 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 8 | SKIP_INSTALL = YES 9 | -------------------------------------------------------------------------------- /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.1 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 | 3.2.4 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 | #endif 4 | 5 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Kingfisher/Kingfisher-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Kingfisher.h" 6 | 7 | FOUNDATION_EXPORT double KingfisherVersionNumber; 8 | FOUNDATION_EXPORT const unsigned char KingfisherVersionString[]; 9 | 10 | -------------------------------------------------------------------------------- /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 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | SWIFT_VERSION = 3.0 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 刘锋婷 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) 2015 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-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements \"$1\"" 63 | /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1" 64 | fi 65 | } 66 | 67 | # Strip invalid architectures 68 | strip_invalid_archs() { 69 | binary="$1" 70 | # Get architectures for current file 71 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 72 | stripped="" 73 | for arch in $archs; do 74 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 75 | # Strip non-valid architectures in-place 76 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 77 | stripped="$stripped $arch" 78 | fi 79 | done 80 | if [[ "$stripped" ]]; then 81 | echo "Stripped $binary of architectures:$stripped" 82 | fi 83 | } 84 | 85 | 86 | if [[ "$CONFIGURATION" == "Debug" ]]; then 87 | install_framework "$BUILT_PRODUCTS_DIR/FTImageSize/FTImageSize.framework" 88 | install_framework "$BUILT_PRODUCTS_DIR/FTIndicator/FTIndicator.framework" 89 | install_framework "$BUILT_PRODUCTS_DIR/FTPickerView/FTPickerView.framework" 90 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 91 | fi 92 | if [[ "$CONFIGURATION" == "Release" ]]; then 93 | install_framework "$BUILT_PRODUCTS_DIR/FTImageSize/FTImageSize.framework" 94 | install_framework "$BUILT_PRODUCTS_DIR/FTIndicator/FTIndicator.framework" 95 | install_framework "$BUILT_PRODUCTS_DIR/FTPickerView/FTPickerView.framework" 96 | install_framework "$BUILT_PRODUCTS_DIR/Kingfisher/Kingfisher.framework" 97 | fi 98 | -------------------------------------------------------------------------------- /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 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | *) 22 | TARGET_DEVICE_ARGS="--target-device mac" 23 | ;; 24 | esac 25 | 26 | install_resource() 27 | { 28 | if [[ "$1" = /* ]] ; then 29 | RESOURCE_PATH="$1" 30 | else 31 | RESOURCE_PATH="${PODS_ROOT}/$1" 32 | fi 33 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 34 | cat << EOM 35 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 36 | EOM 37 | exit 1 38 | fi 39 | case $RESOURCE_PATH in 40 | *.storyboard) 41 | 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}" 42 | 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} 43 | ;; 44 | *.xib) 45 | 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}" 46 | 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} 47 | ;; 48 | *.framework) 49 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 50 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 51 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 52 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | ;; 54 | *.xcdatamodel) 55 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 56 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 57 | ;; 58 | *.xcdatamodeld) 59 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 60 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 61 | ;; 62 | *.xcmappingmodel) 63 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 64 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 65 | ;; 66 | *.xcassets) 67 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 68 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 69 | ;; 70 | *) 71 | echo "$RESOURCE_PATH" 72 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 73 | ;; 74 | esac 75 | } 76 | 77 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 78 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 79 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 80 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | fi 83 | rm -f "$RESOURCES_TO_COPY" 84 | 85 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 86 | then 87 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 88 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 89 | while read line; do 90 | if [[ $line != "${PODS_ROOT}*" ]]; then 91 | XCASSET_FILES+=("$line") 92 | fi 93 | done <<<"$OTHER_XCASSETS" 94 | 95 | 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}" 96 | fi 97 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | 6 | FOUNDATION_EXPORT double Pods_DemoVersionNumber; 7 | FOUNDATION_EXPORT const unsigned char Pods_DemoVersionString[]; 8 | 9 | -------------------------------------------------------------------------------- /Demo/Pods/Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/FTImageSize" "$PODS_CONFIGURATION_BUILD_DIR/FTIndicator" "$PODS_CONFIGURATION_BUILD_DIR/FTPickerView" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | 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" 7 | OTHER_LDFLAGS = $(inherited) -framework "FTImageSize" -framework "FTIndicator" -framework "FTPickerView" -framework "Kingfisher" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 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 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES 3 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/FTImageSize" "$PODS_CONFIGURATION_BUILD_DIR/FTIndicator" "$PODS_CONFIGURATION_BUILD_DIR/FTPickerView" "$PODS_CONFIGURATION_BUILD_DIR/Kingfisher" 4 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 5 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 6 | 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" 7 | OTHER_LDFLAGS = $(inherited) -framework "FTImageSize" -framework "FTIndicator" -framework "FTPickerView" -framework "Kingfisher" 8 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 9 | PODS_BUILD_DIR = $BUILD_DIR 10 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /FTChatMessage/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/FTChatMessage/.DS_Store -------------------------------------------------------------------------------- /FTChatMessage/FTChatMessageCell/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/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([NSFontAttributeName:FTDefaultFontSize,NSParagraphStyleAttributeName: 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 | // Demo 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 | // Demo 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 | 77 | 78 | @objc func iconTapped() { 79 | if (headerViewDelegate != nil) { 80 | headerViewDelegate?.ft_chatMessageHeaderDidTappedOnIcon(messageSenderModel) 81 | } 82 | } 83 | 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /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 | 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 | } 57 | 58 | //MARK: - layoutSubviews - 59 | override func layoutSubviews() { 60 | super.layoutSubviews() 61 | 62 | if self.inputTextView.attributedText.length > 0 { 63 | self.inputTextView.scrollRangeToVisible(NSMakeRange(self.inputTextView.attributedText.length - 1, 1)) 64 | } 65 | } 66 | 67 | //MARK: - recordButtonTapped - 68 | @IBAction func recordButtonTapped(_ sender: UIButton) { 69 | if (inputDelegate != nil) { 70 | inputDelegate!.ft_chatMessageInputViewShouldShowRecordView() 71 | } 72 | } 73 | //MARK: - accessoryButtonTapped - 74 | @IBAction func accessoryButtonTapped(_ sender: UIButton) { 75 | if (inputDelegate != nil) { 76 | inputDelegate!.ft_chatMessageInputViewShouldShowAccessoryView() 77 | } 78 | } 79 | 80 | //MARK: - clearText - 81 | func clearText(){ 82 | inputTextView.text = "" 83 | if (inputDelegate != nil) { 84 | inputDelegate!.ft_chatMessageInputViewShouldUpdateHeight(FTDefaultInputViewHeight) 85 | } 86 | } 87 | 88 | //MARK: - UITextViewDelegate - 89 | func textViewShouldBeginEditing(_ textView: UITextView) -> Bool { 90 | if (inputDelegate != nil) { 91 | inputDelegate!.ft_chatMessageInputViewShouldBeginEditing() 92 | } 93 | return true 94 | } 95 | func textViewShouldEndEditing(_ textView: UITextView) -> Bool { 96 | if (inputDelegate != nil) { 97 | inputDelegate!.ft_chatMessageInputViewShouldEndEditing() 98 | } 99 | return true 100 | } 101 | 102 | func textViewDidChange(_ textView: UITextView) { 103 | if let text : NSAttributedString = textView.attributedText { 104 | let textRect = text.boundingRect(with: CGSize(width: textView.bounds.width - textView.textContainerInset.left - textView.textContainerInset.right, height: CGFloat.greatestFiniteMagnitude), options: [.usesLineFragmentOrigin , .usesFontLeading], context: nil); 105 | 106 | if (inputDelegate != nil) { 107 | inputDelegate!.ft_chatMessageInputViewShouldUpdateHeight(min(max(textRect.height + inputTextViewTopMargin.constant + inputTextViewBottomMargin.constant + textView.textContainerInset.top + textView.textContainerInset.bottom, FTDefaultInputViewHeight), FTDefaultInputViewMaxHeight)) 108 | } 109 | } 110 | } 111 | 112 | func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { 113 | if (text == "\n") { 114 | if (textView.text as NSString).length > 0 { 115 | if (inputDelegate != nil) { 116 | inputDelegate!.ft_chatMessageInputViewShouldDoneWithText(textView.text) 117 | self.clearText() 118 | } 119 | } 120 | return false 121 | } 122 | return true 123 | } 124 | 125 | 126 | } 127 | -------------------------------------------------------------------------------- /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.width } 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 | lazy var messageTableView : UITableView! = { 27 | let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: FTScreenWidth, height: FTScreenHeight), style: .plain) 28 | tableView.separatorStyle = .none 29 | tableView.allowsSelection = false 30 | tableView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.onDrag 31 | tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, FTDefaultInputViewHeight, 0) 32 | tableView.delegate = self 33 | tableView.dataSource = self 34 | 35 | // let header = UIView(frame: CGRect( x: 0, y: 0, width: FTScreenWidth, height: FTDefaultMargin*2)) 36 | // tableView.tableHeaderView = header 37 | 38 | let footer = UIView(frame: CGRect( x: 0, y: 0, width: FTScreenWidth, height: FTDefaultInputViewHeight)) 39 | tableView.tableFooterView = footer 40 | 41 | return tableView 42 | }() 43 | 44 | lazy var messageInputView : FTChatMessageInputView! = { 45 | let inputView : FTChatMessageInputView! = Bundle.main.loadNibNamed("FTChatMessageInputView", owner: nil, options: nil)?[0] as! FTChatMessageInputView 46 | inputView.frame = CGRect(x: 0, y: FTScreenHeight-FTDefaultInputViewHeight, width: FTScreenWidth, height: FTDefaultInputViewHeight) 47 | inputView.inputDelegate = self 48 | return inputView 49 | }() 50 | 51 | lazy var messageRecordView : FTChatMessageRecorderView! = { 52 | let recordView : FTChatMessageRecorderView! = Bundle.main.loadNibNamed("FTChatMessageRecorderView", owner: nil, options: nil)?[0] as! FTChatMessageRecorderView 53 | recordView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 54 | return recordView 55 | }() 56 | 57 | lazy var messageAccessoryView : FTChatMessageAccessoryView! = { 58 | let accessoryView = Bundle.main.loadNibNamed("FTChatMessageAccessoryView", owner: nil, options: nil)?[0] as! FTChatMessageAccessoryView 59 | accessoryView.frame = CGRect(x: 0, y: FTScreenHeight, width: FTScreenWidth, height: FTDefaultAccessoryViewHeight) 60 | return accessoryView 61 | }() 62 | 63 | 64 | 65 | 66 | 67 | override func viewDidLoad() { 68 | super.viewDidLoad() 69 | 70 | self.view.addSubview(messageTableView) 71 | 72 | self.view.addSubview(messageInputView) 73 | 74 | self.view.addSubview(messageRecordView) 75 | 76 | self.view.addSubview(messageAccessoryView) 77 | 78 | DispatchQueue.main.asyncAfter( deadline: DispatchTime.now() + Double(Int64(0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) { 79 | self.scrollToBottom(false) 80 | } 81 | } 82 | 83 | 84 | 85 | override func viewDidAppear(_ animated: Bool) { 86 | super.viewDidAppear(animated) 87 | 88 | 89 | messageAccessoryView.setupAccessoryView() 90 | } 91 | 92 | 93 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 94 | return UIInterfaceOrientationMask.portrait 95 | } 96 | 97 | 98 | internal func addNewMessage(_ message : FTChatMessageModel) { 99 | 100 | chatMessageDataArray.append(message); 101 | 102 | self.origanizeAndReload() 103 | 104 | self.scrollToBottom(true) 105 | 106 | } 107 | 108 | func origanizeAndReload() { 109 | var nastyArray : [[FTChatMessageModel]] = [] 110 | var tempArray : [FTChatMessageModel] = [] 111 | var tempId = "" 112 | for i in 0...chatMessageDataArray.count-1 { 113 | let message = chatMessageDataArray[i] 114 | if message.messageSender.senderId == tempId { 115 | tempArray.append(message) 116 | }else{ 117 | tempId = message.messageSender.senderId; 118 | if tempArray.count > 0 { 119 | nastyArray.append(tempArray) 120 | } 121 | tempArray.removeAll() 122 | tempArray.append(message) 123 | } 124 | if i == chatMessageDataArray.count - 1 { 125 | if tempArray.count > 0 { 126 | nastyArray.append(tempArray) 127 | } 128 | } 129 | } 130 | 131 | self.messageArray = nastyArray 132 | self.messageTableView.reloadData() 133 | } 134 | 135 | 136 | 137 | 138 | } 139 | -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FTChatMessageDemoProject 2 | # It has been moved to [FTChatMessage](https://github.com/liufengting/FTChatMessage). 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ResourceImages/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/ResourceImages/.DS_Store -------------------------------------------------------------------------------- /ResourceImages/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/ResourceImages/1.jpg -------------------------------------------------------------------------------- /ResourceImages/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/ResourceImages/2.jpg -------------------------------------------------------------------------------- /ResourceImages/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/ResourceImages/3.jpg -------------------------------------------------------------------------------- /ResourceImages/ChatMessageDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liufengting/FTChatMessageDemoProject/4fc15d6e64b945d79474960b761f36f3471f3a09/ResourceImages/ChatMessageDemo.gif --------------------------------------------------------------------------------