├── .DS_Store ├── .gitignore ├── PFollow.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── PFollow.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── PFollow ├── AppDelegate.swift ├── Assets.xcassets │ ├── .DS_Store │ ├── Annotation_details │ │ ├── Contents.json │ │ ├── annotation_details_addPhoto.imageset │ │ │ ├── Contents.json │ │ │ └── annotation_details_addPhoto.png │ │ ├── annotation_details_addTime.imageset │ │ │ ├── Contents.json │ │ │ ├── annotation_details_addTime@2x.png │ │ │ └── annotation_details_addTime@3x.png │ │ ├── annotation_details_altitude.imageset │ │ │ ├── Contents.json │ │ │ ├── annotation_details_altitude@2x.png │ │ │ └── annotation_details_altitude@3x.png │ │ ├── annotation_details_env.imageset │ │ │ ├── Contents.json │ │ │ ├── annotation_details_env@2x.png │ │ │ └── annotation_details_env@3x.png │ │ └── annotation_details_save.imageset │ │ │ ├── Contents.json │ │ │ ├── annotation_details_save@2x.png │ │ │ └── annotation_details_save@3x.png │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-Small.png │ │ ├── Icon-Small@2x.png │ │ ├── Icon-Small@3x.png │ │ ├── Icon-Spotlight-40@2x.png │ │ ├── Icon-Spotlight-40@3x.png │ │ ├── Icon.png │ │ ├── Icon@2x.png │ │ └── PLook_logo.png │ ├── BaseViewController │ │ ├── Contents.json │ │ ├── nav_back.imageset │ │ │ ├── Contents.json │ │ │ ├── nav_back@2x.png │ │ │ └── nav_back@3x.png │ │ └── nav_close.imageset │ │ │ ├── Contents.json │ │ │ ├── nav_close@2x.png │ │ │ └── nav_close@3x.png │ ├── Contents.json │ ├── Home_bottom │ │ ├── Contents.json │ │ ├── home_bottom_compass.imageset │ │ │ ├── Contents.json │ │ │ ├── home_bottom_compass@2x.png │ │ │ └── home_bottom_compass@3x.png │ │ ├── home_cloud.imageset │ │ │ ├── Contents.json │ │ │ └── home_cloud.png │ │ ├── home_friend.imageset │ │ │ ├── Contents.json │ │ │ ├── home_friend@2x.png │ │ │ └── home_friend@3x.png │ │ ├── home_places.imageset │ │ │ ├── Contents.json │ │ │ ├── home_places@2x.png │ │ │ └── home_places@3x.png │ │ ├── home_tap.imageset │ │ │ ├── Contents.json │ │ │ ├── home_tap@2x.png │ │ │ └── home_tap@3x.png │ │ ├── home_tap_.imageset │ │ │ ├── Contents.json │ │ │ ├── home_tap@2x.png │ │ │ └── home_tap@3x.png │ │ └── home_tap_ok.imageset │ │ │ ├── Contents.json │ │ │ ├── home_tap_ok@2x.png │ │ │ └── home_tap_ok@3x.png │ ├── Map │ │ ├── .DS_Store │ │ ├── Contents.json │ │ ├── callout_delete.imageset │ │ │ ├── Contents.json │ │ │ ├── callout_delete@2x.png │ │ │ └── callout_delete@3x.png │ │ ├── callout_share.imageset │ │ │ ├── Contents.json │ │ │ ├── callout_share@2x.png │ │ │ └── callout_share@3x.png │ │ ├── home_map_makers_02.imageset │ │ │ ├── Contents.json │ │ │ ├── home_map_makers_02@2x.png │ │ │ └── home_map_makers_02@3x.png │ │ ├── home_map_makers_02_b.imageset │ │ │ ├── Contents.json │ │ │ ├── home_map_makers_02_b@2x.png │ │ │ └── home_map_makers_02_b@3x.png │ │ ├── home_map_makers_03.imageset │ │ │ ├── Contents.json │ │ │ ├── home_map_makers_03@2x.png │ │ │ └── home_map_makers_03@3x.png │ │ ├── home_map_makers_03_b.imageset │ │ │ ├── Contents.json │ │ │ ├── home_map_makers_03_b@2x.png │ │ │ └── home_map_makers_03_b@3x.png │ │ ├── home_map_userlocation.imageset │ │ │ ├── Contents.json │ │ │ ├── home_map_userlocation@2x.png │ │ │ └── home_map_userlocation@3x.png │ │ └── userPosition.imageset │ │ │ ├── Contents.json │ │ │ ├── userPosition.png │ │ │ └── userPosition@2x.png │ ├── Places │ │ ├── Contents.json │ │ ├── places_bgView.imageset │ │ │ ├── Contents.json │ │ │ └── bgView.png │ │ └── places_null.imageset │ │ │ ├── Contents.json │ │ │ └── places_null.png │ └── Weather │ │ ├── Contents.json │ │ ├── 中雨.imageset │ │ ├── Contents.json │ │ └── 中雨.png │ │ ├── 中雨转大雨.imageset │ │ ├── Contents.json │ │ └── 中雨转大雨.png │ │ ├── 中雪.imageset │ │ ├── Contents.json │ │ └── 中雪.png │ │ ├── 中雪转大雪.imageset │ │ ├── Contents.json │ │ └── 中雪转大雪.png │ │ ├── 冻雨.imageset │ │ ├── Contents.json │ │ └── 冻雨.png │ │ ├── 多云.imageset │ │ ├── Contents.json │ │ └── 多云.png │ │ ├── 大暴雨.imageset │ │ ├── Contents.json │ │ └── 大暴雨.png │ │ ├── 大雨.imageset │ │ ├── Contents.json │ │ └── 大雨.png │ │ ├── 大雨转暴雨.imageset │ │ ├── Contents.json │ │ └── 大雨转暴雨.png │ │ ├── 大雪.imageset │ │ ├── Contents.json │ │ └── 大雪.png │ │ ├── 大雪转暴雪.imageset │ │ ├── Contents.json │ │ └── 大雪转暴雪.png │ │ ├── 小雨.imageset │ │ ├── Contents.json │ │ └── 小雨.png │ │ ├── 小雨转中雨.imageset │ │ ├── Contents.json │ │ └── 小雨转中雨.png │ │ ├── 小雪.imageset │ │ ├── Contents.json │ │ └── 小雪.png │ │ ├── 小雪转中雪.imageset │ │ ├── Contents.json │ │ └── 小雪转中雪.png │ │ ├── 扬沙.imageset │ │ ├── Contents.json │ │ └── 扬沙.png │ │ ├── 晴.imageset │ │ ├── Contents.json │ │ └── 晴.png │ │ ├── 暴雨.imageset │ │ ├── Contents.json │ │ └── 暴雨.png │ │ ├── 暴雨转大暴雨.imageset │ │ ├── Contents.json │ │ └── 暴雨转大暴雨.png │ │ ├── 沙尘暴.imageset │ │ ├── Contents.json │ │ └── 沙尘暴.png │ │ ├── 浮尘.imageset │ │ ├── Contents.json │ │ └── 浮尘.png │ │ ├── 特大暴雨.imageset │ │ ├── Contents.json │ │ └── 特大暴雨.png │ │ ├── 特大沙尘暴.imageset │ │ ├── Contents.json │ │ └── 特大沙尘暴.png │ │ ├── 阴.imageset │ │ ├── Contents.json │ │ └── 阴.png │ │ ├── 阵雨.imageset │ │ ├── Contents.json │ │ └── 阵雨.png │ │ ├── 阵雪.imageset │ │ ├── Contents.json │ │ └── 阵雪.png │ │ ├── 雨夹雪.imageset │ │ ├── Contents.json │ │ └── 雨夹雪.png │ │ ├── 雷阵雨.imageset │ │ ├── Contents.json │ │ └── 雷阵雨.png │ │ ├── 雷雨冰雹.imageset │ │ ├── Contents.json │ │ └── 雷雨冰雹.png │ │ ├── 雾.imageset │ │ ├── Contents.json │ │ └── 雾.png │ │ └── 雾霾.imageset │ │ ├── Contents.json │ │ └── 雾霾.png ├── Base.lproj │ └── LaunchScreen.storyboard ├── Common │ ├── PJBaseViewController.swift │ ├── PJCoreDataHelper.swift │ ├── PJDefine.swift │ ├── PJTapic.swift │ └── PJTools-UIView+Position.swift ├── Info.plist ├── PFollow-Bridging-Header.h ├── PFollow.entitlements ├── PFollow.xcdatamodeld │ ├── .xccurrentversion │ └── PFollow.xcdatamodel │ │ └── contents ├── Page │ ├── Home │ │ ├── controller │ │ │ └── PJHomeViewController.swift │ │ ├── model │ │ │ └── AnnotationModel.swift │ │ └── view │ │ │ ├── MapView │ │ │ ├── PJHomeMapAnnotetionView.swift │ │ │ ├── PJHomeMapCalloutView.swift │ │ │ └── PJHomeMapView.swift │ │ │ └── bottomView │ │ │ └── PJHomeBottomView.swift │ ├── InfoFlow │ │ └── controller │ │ │ ├── Details │ │ │ ├── Controller │ │ │ │ └── PJAnnotationDetailsViewController.swift │ │ │ └── View │ │ │ │ ├── PJDatePickerView.swift │ │ │ │ └── PJDatePickerView.xib │ │ │ └── Flow │ │ │ └── PJInfoFlowViewController.swift │ └── Places │ │ ├── Controller │ │ └── PJPlacesViewController.swift │ │ └── View │ │ └── Scene │ │ └── PJPlacesAnnotationScene.swift └── mapView.data ├── PFollowTests ├── Info.plist └── PFollowTests.swift ├── PFollowUITests ├── Info.plist └── PFollowUITests.swift ├── Podfile.lock ├── README.md ├── banner.png └── podfile /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/.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 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | # Package.resolved 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | Pods 50 | !Podfile 51 | !Podfile.lock 52 | # 53 | # Add this line if you want to avoid checking in source code from the Xcode workspace 54 | # *.xcworkspace 55 | 56 | # Carthage 57 | # 58 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 59 | # Carthage/Checkouts 60 | 61 | Carthage/Build 62 | 63 | # fastlane 64 | # 65 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 66 | # screenshots whenever they are needed. 67 | # For more information about the recommended setup visit: 68 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 69 | 70 | fastlane/report.xml 71 | fastlane/Preview.html 72 | fastlane/screenshots/**/*.png 73 | fastlane/test_output 74 | 75 | # Code Injection 76 | # 77 | # After new code Injection tools there's a generated folder /iOSInjectionProject 78 | # https://github.com/johnno1962/injectionforxcode 79 | 80 | iOSInjectionProject/ 81 | 82 | -------------------------------------------------------------------------------- /PFollow.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PFollow.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PFollow.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PFollow.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PFollow/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | import IQKeyboardManagerSwift 13 | 14 | @UIApplicationMain 15 | class AppDelegate: UIResponder, UIApplicationDelegate { 16 | 17 | var window: UIWindow? 18 | 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 21 | 22 | IQKeyboardManager.shared.enable = true 23 | 24 | window = UIWindow.init(frame: UIScreen.main.bounds) 25 | let navigationControllrt = UINavigationController.init(rootViewController: PJHomeViewController()) 26 | window?.rootViewController = navigationControllrt 27 | window?.makeKeyAndVisible() 28 | 29 | AMapServices.shared().apiKey = "9481ea9de167f7b11a192b11b620dd84" 30 | 31 | return true 32 | } 33 | 34 | func applicationWillResignActive(_ application: UIApplication) { 35 | // 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. 36 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 37 | } 38 | 39 | func applicationDidEnterBackground(_ application: UIApplication) { 40 | // 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. 41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 42 | } 43 | 44 | func applicationWillEnterForeground(_ application: UIApplication) { 45 | // 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. 46 | } 47 | 48 | func applicationDidBecomeActive(_ application: UIApplication) { 49 | // 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. 50 | } 51 | 52 | func applicationWillTerminate(_ application: UIApplication) { 53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 54 | // Saves changes in the application's managed object context before the application terminates. 55 | self.saveContext() 56 | } 57 | 58 | // MARK: - Core Data stack 59 | 60 | lazy var persistentContainer: NSPersistentContainer = { 61 | /* 62 | The persistent container for the application. This implementation 63 | creates and returns a container, having loaded the store for the 64 | application to it. This property is optional since there are legitimate 65 | error conditions that could cause the creation of the store to fail. 66 | */ 67 | let container = NSPersistentContainer(name: "PFollow") 68 | container.loadPersistentStores(completionHandler: { (storeDescription, error) in 69 | if let error = error as NSError? { 70 | // Replace this implementation with code to handle the error appropriately. 71 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 72 | 73 | /* 74 | Typical reasons for an error here include: 75 | * The parent directory does not exist, cannot be created, or disallows writing. 76 | * The persistent store is not accessible, due to permissions or data protection when the device is locked. 77 | * The device is out of space. 78 | * The store could not be migrated to the current model version. 79 | Check the error message to determine what the actual problem was. 80 | */ 81 | fatalError("Unresolved error \(error), \(error.userInfo)") 82 | } 83 | }) 84 | return container 85 | }() 86 | 87 | // MARK: - Core Data Saving support 88 | 89 | func saveContext () { 90 | let context = persistentContainer.viewContext 91 | if context.hasChanges { 92 | do { 93 | try context.save() 94 | } catch { 95 | // Replace this implementation with code to handle the error appropriately. 96 | // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 97 | let nserror = error as NSError 98 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)") 99 | } 100 | } 101 | } 102 | 103 | } 104 | 105 | -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_addPhoto.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "annotation_details_addPhoto.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_addPhoto.imageset/annotation_details_addPhoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_addPhoto.imageset/annotation_details_addPhoto.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_addTime.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "annotation_details_addTime@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "annotation_details_addTime@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_addTime.imageset/annotation_details_addTime@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_addTime.imageset/annotation_details_addTime@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_addTime.imageset/annotation_details_addTime@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_addTime.imageset/annotation_details_addTime@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_altitude.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "annotation_details_altitude@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "annotation_details_altitude@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_altitude.imageset/annotation_details_altitude@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_altitude.imageset/annotation_details_altitude@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_altitude.imageset/annotation_details_altitude@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_altitude.imageset/annotation_details_altitude@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_env.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "annotation_details_env@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "annotation_details_env@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_env.imageset/annotation_details_env@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_env.imageset/annotation_details_env@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_env.imageset/annotation_details_env@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_env.imageset/annotation_details_env@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_save.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "annotation_details_save@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "annotation_details_save@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_save.imageset/annotation_details_save@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_save.imageset/annotation_details_save@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Annotation_details/annotation_details_save.imageset/annotation_details_save@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Annotation_details/annotation_details_save.imageset/annotation_details_save@3x.png -------------------------------------------------------------------------------- /PFollow/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 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-Small.png", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-Small@2x.png", 23 | "scale" : "2x" 24 | }, 25 | { 26 | "size" : "29x29", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-Small@3x.png", 29 | "scale" : "3x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-Spotlight-40@2x.png", 35 | "scale" : "2x" 36 | }, 37 | { 38 | "size" : "40x40", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-Spotlight-40@3x.png", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "size" : "57x57", 45 | "idiom" : "iphone", 46 | "filename" : "Icon.png", 47 | "scale" : "1x" 48 | }, 49 | { 50 | "size" : "57x57", 51 | "idiom" : "iphone", 52 | "filename" : "Icon@2x.png", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "size" : "60x60", 57 | "idiom" : "iphone", 58 | "filename" : "Icon-60@2x.png", 59 | "scale" : "2x" 60 | }, 61 | { 62 | "size" : "60x60", 63 | "idiom" : "iphone", 64 | "filename" : "Icon-60@3x.png", 65 | "scale" : "3x" 66 | }, 67 | { 68 | "idiom" : "ipad", 69 | "size" : "20x20", 70 | "scale" : "1x" 71 | }, 72 | { 73 | "idiom" : "ipad", 74 | "size" : "20x20", 75 | "scale" : "2x" 76 | }, 77 | { 78 | "idiom" : "ipad", 79 | "size" : "29x29", 80 | "scale" : "1x" 81 | }, 82 | { 83 | "idiom" : "ipad", 84 | "size" : "29x29", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "idiom" : "ipad", 89 | "size" : "40x40", 90 | "scale" : "1x" 91 | }, 92 | { 93 | "idiom" : "ipad", 94 | "size" : "40x40", 95 | "scale" : "2x" 96 | }, 97 | { 98 | "idiom" : "ipad", 99 | "size" : "76x76", 100 | "scale" : "1x" 101 | }, 102 | { 103 | "idiom" : "ipad", 104 | "size" : "76x76", 105 | "scale" : "2x" 106 | }, 107 | { 108 | "idiom" : "ipad", 109 | "size" : "83.5x83.5", 110 | "scale" : "2x" 111 | }, 112 | { 113 | "size" : "1024x1024", 114 | "idiom" : "ios-marketing", 115 | "filename" : "PLook_logo.png", 116 | "scale" : "1x" 117 | } 118 | ], 119 | "info" : { 120 | "version" : 1, 121 | "author" : "xcode" 122 | } 123 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/AppIcon.appiconset/PLook_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/AppIcon.appiconset/PLook_logo.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "nav_back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "nav_back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_back.imageset/nav_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/BaseViewController/nav_back.imageset/nav_back@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_back.imageset/nav_back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/BaseViewController/nav_back.imageset/nav_back@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_close.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "nav_close@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "nav_close@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_close.imageset/nav_close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/BaseViewController/nav_close.imageset/nav_close@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/BaseViewController/nav_close.imageset/nav_close@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/BaseViewController/nav_close.imageset/nav_close@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_bottom_compass.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_bottom_compass@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_bottom_compass@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_bottom_compass.imageset/home_bottom_compass@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_bottom_compass.imageset/home_bottom_compass@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_bottom_compass.imageset/home_bottom_compass@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_bottom_compass.imageset/home_bottom_compass@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_cloud.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "home_cloud.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_cloud.imageset/home_cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_cloud.imageset/home_cloud.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_friend.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_friend@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_friend@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_friend.imageset/home_friend@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_friend.imageset/home_friend@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_friend.imageset/home_friend@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_friend.imageset/home_friend@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_places.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_places@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_places@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_places.imageset/home_places@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_places.imageset/home_places@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_places.imageset/home_places@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_places.imageset/home_places@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_tap@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_tap@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap.imageset/home_tap@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap.imageset/home_tap@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap.imageset/home_tap@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap.imageset/home_tap@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_tap@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_tap@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_.imageset/home_tap@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap_.imageset/home_tap@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_.imageset/home_tap@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap_.imageset/home_tap@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_ok.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_tap_ok@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_tap_ok@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_ok.imageset/home_tap_ok@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap_ok.imageset/home_tap_ok@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Home_bottom/home_tap_ok.imageset/home_tap_ok@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Home_bottom/home_tap_ok.imageset/home_tap_ok@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/.DS_Store -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "callout_delete@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "callout_delete@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_delete.imageset/callout_delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/callout_delete.imageset/callout_delete@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_delete.imageset/callout_delete@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/callout_delete.imageset/callout_delete@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_share.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "callout_share@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "callout_share@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_share.imageset/callout_share@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/callout_share.imageset/callout_share@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/callout_share.imageset/callout_share@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/callout_share.imageset/callout_share@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_map_makers_02@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_map_makers_02@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02.imageset/home_map_makers_02@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_02.imageset/home_map_makers_02@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02.imageset/home_map_makers_02@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_02.imageset/home_map_makers_02@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02_b.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_map_makers_02_b@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_map_makers_02_b@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02_b.imageset/home_map_makers_02_b@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_02_b.imageset/home_map_makers_02_b@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_02_b.imageset/home_map_makers_02_b@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_02_b.imageset/home_map_makers_02_b@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_map_makers_03@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_map_makers_03@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03.imageset/home_map_makers_03@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_03.imageset/home_map_makers_03@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03.imageset/home_map_makers_03@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_03.imageset/home_map_makers_03@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03_b.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_map_makers_03_b@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_map_makers_03_b@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03_b.imageset/home_map_makers_03_b@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_03_b.imageset/home_map_makers_03_b@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_makers_03_b.imageset/home_map_makers_03_b@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_makers_03_b.imageset/home_map_makers_03_b@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_userlocation.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "home_map_userlocation@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "home_map_userlocation@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_userlocation.imageset/home_map_userlocation@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_userlocation.imageset/home_map_userlocation@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/home_map_userlocation.imageset/home_map_userlocation@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/home_map_userlocation.imageset/home_map_userlocation@3x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/userPosition.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "userPosition.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "userPosition@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/userPosition.imageset/userPosition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/userPosition.imageset/userPosition.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Map/userPosition.imageset/userPosition@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Map/userPosition.imageset/userPosition@2x.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Places/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Places/places_bgView.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bgView.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Places/places_bgView.imageset/bgView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Places/places_bgView.imageset/bgView.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Places/places_null.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "places_null.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Places/places_null.imageset/places_null.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Places/places_null.imageset/places_null.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "中雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雨.imageset/中雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/中雨.imageset/中雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雨转大雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "中雨转大雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雨转大雨.imageset/中雨转大雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/中雨转大雨.imageset/中雨转大雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "中雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雪.imageset/中雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/中雪.imageset/中雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雪转大雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "中雪转大雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/中雪转大雪.imageset/中雪转大雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/中雪转大雪.imageset/中雪转大雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/冻雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "冻雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/冻雨.imageset/冻雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/冻雨.imageset/冻雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/多云.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "多云.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/多云.imageset/多云.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/多云.imageset/多云.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大暴雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "大暴雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大暴雨.imageset/大暴雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/大暴雨.imageset/大暴雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "大雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雨.imageset/大雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/大雨.imageset/大雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雨转暴雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "大雨转暴雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雨转暴雨.imageset/大雨转暴雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/大雨转暴雨.imageset/大雨转暴雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "大雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雪.imageset/大雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/大雪.imageset/大雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雪转暴雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "大雪转暴雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/大雪转暴雪.imageset/大雪转暴雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/大雪转暴雪.imageset/大雪转暴雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "小雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雨.imageset/小雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/小雨.imageset/小雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雨转中雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "小雨转中雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雨转中雨.imageset/小雨转中雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/小雨转中雨.imageset/小雨转中雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "小雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雪.imageset/小雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/小雪.imageset/小雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雪转中雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "小雪转中雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/小雪转中雪.imageset/小雪转中雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/小雪转中雪.imageset/小雪转中雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/扬沙.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "扬沙.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/扬沙.imageset/扬沙.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/扬沙.imageset/扬沙.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/晴.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "晴.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/晴.imageset/晴.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/晴.imageset/晴.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/暴雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "暴雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/暴雨.imageset/暴雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/暴雨.imageset/暴雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/暴雨转大暴雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "暴雨转大暴雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/暴雨转大暴雨.imageset/暴雨转大暴雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/暴雨转大暴雨.imageset/暴雨转大暴雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/沙尘暴.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "沙尘暴.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/沙尘暴.imageset/沙尘暴.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/沙尘暴.imageset/沙尘暴.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/浮尘.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "浮尘.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/浮尘.imageset/浮尘.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/浮尘.imageset/浮尘.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/特大暴雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "特大暴雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/特大暴雨.imageset/特大暴雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/特大暴雨.imageset/特大暴雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/特大沙尘暴.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "特大沙尘暴.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/特大沙尘暴.imageset/特大沙尘暴.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/特大沙尘暴.imageset/特大沙尘暴.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阴.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "阴.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阴.imageset/阴.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/阴.imageset/阴.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阵雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "阵雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阵雨.imageset/阵雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/阵雨.imageset/阵雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阵雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "阵雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/阵雪.imageset/阵雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/阵雪.imageset/阵雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雨夹雪.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "雨夹雪.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雨夹雪.imageset/雨夹雪.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/雨夹雪.imageset/雨夹雪.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雷阵雨.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "雷阵雨.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雷阵雨.imageset/雷阵雨.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/雷阵雨.imageset/雷阵雨.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雷雨冰雹.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "雷雨冰雹.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雷雨冰雹.imageset/雷雨冰雹.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/雷雨冰雹.imageset/雷雨冰雹.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雾.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "雾.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雾.imageset/雾.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/雾.imageset/雾.png -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雾霾.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "雾霾.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 | } -------------------------------------------------------------------------------- /PFollow/Assets.xcassets/Weather/雾霾.imageset/雾霾.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/Assets.xcassets/Weather/雾霾.imageset/雾霾.png -------------------------------------------------------------------------------- /PFollow/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 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /PFollow/Common/PJBaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJBaseViewController.swift 3 | // PFollow 4 | // 5 | // Created by PJHubs on 2018/8/27. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PJBaseViewController: UIViewController { 12 | 13 | var headerView: UIView? 14 | var navBarHeigt: CGFloat? 15 | var rightBarButton: UIButton? 16 | var leftBarButton: UIButton? 17 | 18 | 19 | // MARK: - life cycle 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default) 24 | navigationController?.navigationBar.shadowImage = UIImage() 25 | navigationController?.navigationBar.isTranslucent = true 26 | navigationController?.navigationBar.barStyle = .black 27 | // 自定义了 leftBarButtonItem ,需要 28 | navigationController?.interactivePopGestureRecognizer?.isEnabled = true 29 | navigationController?.interactivePopGestureRecognizer?.delegate = nil 30 | 31 | initView() 32 | } 33 | 34 | 35 | override func didReceiveMemoryWarning() { 36 | super.didReceiveMemoryWarning() 37 | } 38 | 39 | 40 | // MARK: life cycle 41 | private func initView() { 42 | navBarHeigt = navigationController!.navigationBar.height + PJStatusHeight 43 | 44 | headerView = UIView(frame: CGRect(x: 0, y: 0, width: view.width, 45 | height: navBarHeigt!)) 46 | view.addSubview(headerView!) 47 | } 48 | 49 | 50 | func leftBarButtonItemAction(action: Selector) { 51 | let leftButton = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) 52 | leftButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: -15, bottom: 0, right: 0) 53 | leftButton.setImage(UIImage(named: "nav_back"), for: .normal) 54 | leftButton.addTarget(self, action: action, for: .touchUpInside) 55 | let leftBarButtonItem = UIBarButtonItem(customView: leftButton) 56 | navigationItem.leftBarButtonItem = leftBarButtonItem 57 | } 58 | 59 | 60 | func rightBarButtonItem(imageName: String, action: Selector) { 61 | let rightButton = UIButton(frame: CGRect(x: 0, y: 0, width: 40, height: 40)) 62 | rightButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -15) 63 | rightButton.setImage(UIImage(named: imageName), for: .normal) 64 | rightButton.addTarget(self, action: action, for: .touchUpInside) 65 | let rightBarButtonItem = UIBarButtonItem(customView: rightButton) 66 | rightBarButton = rightButton 67 | navigationItem.rightBarButtonItem = rightBarButtonItem 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /PFollow/Common/PJCoreDataHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJCoreDataHelper.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/18. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | class PJCoreDataHelper: NSObject { 13 | 14 | // 单例 15 | static let shared = PJCoreDataHelper() 16 | 17 | private let delegate = UIApplication.shared.delegate as! AppDelegate 18 | private let context: NSManagedObjectContext? 19 | 20 | 21 | // MAEK: life Cycle 22 | override init() { 23 | context = delegate.persistentContainer.viewContext 24 | } 25 | 26 | 27 | func addAnnotation(model: AnnotationModel) -> Bool { 28 | let annotationEntity = NSEntityDescription.insertNewObject(forEntityName: "Annotation", into: context!) as! Annotation 29 | annotationEntity.created_time = model.createdTimeString 30 | annotationEntity.weather = model.weatherString 31 | annotationEntity.environment = model.environmentString 32 | annotationEntity.latitude = model.latitude 33 | annotationEntity.longitude = model.longitude 34 | annotationEntity.altitude = model.altitude 35 | annotationEntity.stepCount = model.stepCount 36 | annotationEntity.city = model.city 37 | annotationEntity.formatterAddress = model.formatterAddress 38 | annotationEntity.markerName = model.markerName 39 | 40 | do { 41 | try context?.save() 42 | print("保存成功") 43 | return true 44 | } catch { 45 | print("不能保存:\(error)") 46 | return false 47 | } 48 | } 49 | 50 | 51 | func allAnnotation() -> [AnnotationModel] { 52 | var annotationArray = [AnnotationModel]() 53 | let fetchRequest = NSFetchRequest(entityName:"Annotation") 54 | do { 55 | let fetchedObjects = try context?.fetch(fetchRequest) 56 | for info in fetchedObjects!{ 57 | let data = [ 58 | "createdTimeString": info.created_time, 59 | "weatherString": info.weather, 60 | "environmentString": info.environment, 61 | "latitude": info.latitude, 62 | "longitude": info.longitude, 63 | "altitude": info.altitude, 64 | "stepCount": info.stepCount, 65 | "city": info.city, 66 | "formatterAddress": info.formatterAddress, 67 | "markerName": info.markerName, 68 | ] 69 | 70 | if let json = try? JSONSerialization.data(withJSONObject: data, options: []) { 71 | if let annotationModel = try? JSONDecoder().decode(AnnotationModel.self, from: json) { 72 | annotationArray.append(annotationModel) 73 | } 74 | } 75 | } 76 | print("查询成功") 77 | return annotationArray 78 | } 79 | catch { 80 | print("查询失败:\(error)") 81 | return [] 82 | } 83 | } 84 | 85 | 86 | func updateAnnotation(formatString: String, updateTime: String) -> Bool { 87 | let fetchRequest = NSFetchRequest(entityName:"Annotation") 88 | let predict = NSPredicate(format: formatString, argumentArray: nil) 89 | fetchRequest.predicate = predict 90 | do { 91 | let annotation = try context?.fetch(fetchRequest)[0] 92 | annotation?.created_time = updateTime 93 | do { 94 | try context?.save() 95 | print("保存成功") 96 | return true 97 | } catch { 98 | print("不能保存:\(error)") 99 | return false 100 | } 101 | } catch { 102 | print("查询成功") 103 | return false 104 | } 105 | } 106 | 107 | 108 | func deleteAnnotation(model: AnnotationModel) { 109 | let fetchRequest = NSFetchRequest(entityName:"Annotation") 110 | let coordinate = NSPredicate(format: "longitude=\(model.longitude) and latitude=\(model.latitude)") 111 | fetchRequest.predicate = coordinate 112 | do { 113 | let fetchedObjects = try context?.fetch(fetchRequest) 114 | for instance in fetchedObjects! { 115 | context?.delete(instance) 116 | } 117 | } catch { 118 | print("删除查询出错:\(error)") 119 | print("查询成功") 120 | } 121 | do { 122 | try context?.save() 123 | print("删除成功") 124 | } catch { 125 | print("删除出错:\(error)") 126 | } 127 | } 128 | 129 | 130 | func annotationContent(model: AnnotationModel) -> String { 131 | let fetchRequest = NSFetchRequest(entityName:"AnnotationContent") 132 | let contentPredicate = NSPredicate(format: "longitude=\(model.longitude) and latitude=\(model.latitude)") 133 | fetchRequest.predicate = contentPredicate 134 | do { 135 | let fetchedObjects = try context?.fetch(fetchRequest) 136 | print("查询成功") 137 | return fetchedObjects?.first?.content ?? "" 138 | } 139 | catch { 140 | print("查询失败:\(error)") 141 | return "" 142 | } 143 | } 144 | 145 | 146 | func addAnnotationContent(content: String, model: AnnotationModel) -> Bool { 147 | let fetchRequest = NSFetchRequest(entityName:"AnnotationContent") 148 | let contentPredicate = NSPredicate(format: "longitude=\(model.longitude) and latitude=\(model.latitude)") 149 | fetchRequest.predicate = contentPredicate 150 | do { 151 | let fetchedObjects = try context?.fetch(fetchRequest) 152 | print("查询成功") 153 | 154 | let object = fetchedObjects?.first 155 | 156 | if object?.content != nil { 157 | context?.delete(object!) 158 | } 159 | 160 | let annotationEntity = NSEntityDescription.insertNewObject(forEntityName: "AnnotationContent", into: context!) as! AnnotationContent 161 | annotationEntity.content = content 162 | annotationEntity.longitude = model.longitude 163 | annotationEntity.latitude = model.latitude 164 | 165 | do { 166 | try context?.save() 167 | print("保存成功") 168 | return true 169 | } catch { 170 | print("不能保存:\(error)") 171 | return false 172 | } 173 | } 174 | catch { 175 | print("查询失败:\(error)") 176 | return false 177 | } 178 | } 179 | 180 | 181 | func addAnnotationPhoto(photoImage: UIImage, model: AnnotationModel) -> Bool { 182 | 183 | _ = deleteAnnotationImage(model: model) 184 | 185 | let annotationEntity = NSEntityDescription.insertNewObject(forEntityName: "AnnotationImage", into: context!) as! AnnotationImage 186 | annotationEntity.image = UIImageJPEGRepresentation(photoImage, 1) 187 | annotationEntity.longitude = model.longitude 188 | annotationEntity.latitude = model.latitude 189 | 190 | do { 191 | try context?.save() 192 | print("保存成功") 193 | return true 194 | } catch { 195 | print("不能保存:\(error)") 196 | return false 197 | } 198 | } 199 | 200 | 201 | func annotationImage(model: AnnotationModel) -> UIImage? { 202 | let fetchRequest = NSFetchRequest(entityName:"AnnotationImage") 203 | let contentPredicate = NSPredicate(format: "longitude=\(model.longitude) and latitude=\(model.latitude)") 204 | fetchRequest.predicate = contentPredicate 205 | do { 206 | let fetchedObjects = try context?.fetch(fetchRequest) 207 | print("查询成功") 208 | if fetchedObjects?.first != nil { 209 | return UIImage(data: fetchedObjects!.first!.image!)! 210 | } else { 211 | return nil 212 | } 213 | } 214 | catch { 215 | print("查询失败:\(error)") 216 | return nil 217 | } 218 | } 219 | 220 | 221 | func deleteAnnotationImage(model: AnnotationModel) -> Bool { 222 | let fetchRequest = NSFetchRequest(entityName:"AnnotationImage") 223 | let contentPredicate = NSPredicate(format: "longitude=\(model.longitude) and latitude=\(model.latitude)") 224 | fetchRequest.predicate = contentPredicate 225 | do { 226 | let fetchedObjects = try context?.fetch(fetchRequest) 227 | for instance in fetchedObjects! { 228 | context?.delete(instance) 229 | } 230 | 231 | print("查询成功") 232 | return true 233 | } 234 | catch { 235 | print("查询失败:\(error)") 236 | return false 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /PFollow/Common/PJDefine.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJDefine.swift 3 | // DiDiData 4 | // 5 | // Created by PJ on 2018/4/24. 6 | // Copyright © 2018年 Didi.Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Foundation 11 | 12 | 13 | // MARK: - 设备 14 | let PJSCREEN_HEIGHT = CGFloat(UIScreen.main.bounds.height) 15 | let PJSCREEN_WIDTH = CGFloat(UIScreen.main.bounds.width) 16 | let PJTABBAR_HEIGHT = CGFloat(48) 17 | let PJStatusHeight = UIApplication.shared.statusBarFrame.size.height 18 | 19 | // 不能直接这么写死 34 20 | let PJBottomLinerHeight = iPhoneX ? CGFloat(34) : 0 21 | let iPhoneX = PJSCREEN_HEIGHT == 812 22 | 23 | func PJDeviceWithPortrait() -> Bool { 24 | return UIDevice.current.orientation == .portrait || UIDevice.current.orientation == .portraitUpsideDown 25 | } 26 | 27 | func PJDevice() -> String { 28 | let currentScreen = UIScreen.main.currentMode?.size 29 | if currentScreen == CGSize.init(width: 1125, height: 2436) { 30 | return "iPhoneX" 31 | } else { 32 | return "None" 33 | } 34 | } 35 | 36 | func PJUILength(length: Int) -> CGFloat { 37 | return PJSCREEN_WIDTH * CGFloat(length) / 375 38 | } 39 | 40 | 41 | // MARK: - 位置 42 | func x(object: UIView) -> CGFloat { 43 | return object.frame.origin.x 44 | } 45 | func y(object: UIView) -> CGFloat { 46 | return object.frame.origin.y 47 | } 48 | func w(object: UIView) -> CGFloat { 49 | return object.frame.size.width 50 | } 51 | func h(object: UIView) -> CGFloat { 52 | return object.frame.size.height 53 | } 54 | 55 | 56 | // MARK: - 字符串 57 | func getStringLength(string: String) -> CGFloat { 58 | let count = string.count; 59 | if inputLetterAndSpace(string) { 60 | return CGFloat(9 * count) 61 | } 62 | return CGFloat(16 * count) 63 | } 64 | 65 | func inputLetterAndSpace(_ string: String) -> Bool { 66 | let regex = "[ a-zA-Z]*" 67 | let predicate = NSPredicate(format: "SELF MATCHES %@", regex) 68 | let inputString = predicate.evaluate(with: string) 69 | return inputString 70 | } 71 | 72 | 73 | // MARK: - 颜色 74 | func PJRGB(r: CGFloat, g:CGFloat, b:CGFloat) -> UIColor { 75 | return UIColor.init(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: 1) 76 | } 77 | 78 | 79 | // MARK: - 视图 80 | func PJInsertRoundingCorners(_ view: UIView) { 81 | let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 8.0, height: 8.0)) 82 | let pathMaskLayer = CAShapeLayer() 83 | pathMaskLayer.frame = view.bounds 84 | pathMaskLayer.path = path.cgPath 85 | view.layer.mask = pathMaskLayer 86 | } 87 | 88 | // MARK: - 通知 89 | let PJNotificationName_changeLanguage = "PJNotificationNameChangeLanguage" 90 | let PJNotificationName_appOut = "PJNotificationNameAppOut" 91 | let PJNotificationName_network = "kONEONENetworkStatusChange" 92 | let PJNotificationName_updateCallouView = "PJNotitficationNameUpdateCallout" 93 | 94 | 95 | -------------------------------------------------------------------------------- /PFollow/Common/PJTapic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJTapic.swift 3 | // Bonfire 4 | // 5 | // Created by pjpjpj on 2018/5/19. 6 | // Copyright © 2018年 #incloud. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PJTapic: NSObject { 12 | 13 | class func select() { 14 | let g = UISelectionFeedbackGenerator.init() 15 | g.selectionChanged() 16 | g.prepare() 17 | } 18 | 19 | class func succee() { 20 | let g = UINotificationFeedbackGenerator.init() 21 | g.notificationOccurred(.success) 22 | g.prepare() 23 | } 24 | 25 | class func warning() { 26 | let g = UINotificationFeedbackGenerator.init() 27 | g.notificationOccurred(.warning) 28 | g.prepare() 29 | } 30 | 31 | class func error() { 32 | let g = UINotificationFeedbackGenerator.init() 33 | g.notificationOccurred(.error) 34 | g.prepare() 35 | } 36 | 37 | class func tap() { 38 | UIImpactFeedbackGenerator.init(style: .light).impactOccurred() 39 | } 40 | 41 | class func tipsTap() { 42 | // if crash, use AudioServicesPlaySystemSound(1519) 43 | UIImpactFeedbackGenerator.init(style: .heavy).impactOccurred() 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /PFollow/Common/PJTools-UIView+Position.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJTools-UIView+Position.swift 3 | // DiDiData 4 | // 5 | // Created by PJ on 2018/4/26. 6 | // Copyright © 2018年 Didi.Inc. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | static private let PJSCREEN_SCALE = UIScreen.main.scale 14 | 15 | private func getPixintegral(pointValue: CGFloat) -> CGFloat { 16 | return round(pointValue * UIView.PJSCREEN_SCALE) / UIView.PJSCREEN_SCALE 17 | } 18 | 19 | public var x: CGFloat { 20 | get { 21 | return self.frame.origin.x 22 | } 23 | set(x) { 24 | self.frame = CGRect.init( 25 | x: getPixintegral(pointValue: x), 26 | y: self.y, 27 | width: self.width, 28 | height: self.height 29 | ) 30 | } 31 | } 32 | 33 | public var y: CGFloat { 34 | get { 35 | return self.frame.origin.y 36 | } 37 | set(y) { 38 | self.frame = CGRect.init( 39 | x: self.x, 40 | y: getPixintegral(pointValue: y), 41 | width: self.width, 42 | height: self.height 43 | ) 44 | } 45 | } 46 | 47 | public var width: CGFloat { 48 | get { 49 | return self.frame.size.width 50 | } 51 | set(width) { 52 | self.frame = CGRect.init( 53 | x: self.x, 54 | y: self.y, 55 | width: getPixintegral(pointValue: width), 56 | height: self.height 57 | ) 58 | } 59 | } 60 | 61 | public var height: CGFloat { 62 | get { 63 | return self.frame.size.height 64 | } 65 | set (height) { 66 | self.frame = CGRect.init( 67 | x: self.x, 68 | y: self.y, 69 | width: self.width, 70 | height: getPixintegral(pointValue: height) 71 | ) 72 | } 73 | } 74 | 75 | public var bottom: CGFloat { 76 | get { 77 | return self.y + self.height 78 | } 79 | set(bottom) { 80 | self.y = bottom - self.height 81 | } 82 | } 83 | 84 | public var right: CGFloat { 85 | get { 86 | return self.x + self.width 87 | } 88 | set (right) { 89 | self.x = right - self.width 90 | } 91 | } 92 | 93 | public var left: CGFloat { 94 | get { 95 | return self.x 96 | } 97 | set(left) { 98 | self.x = left 99 | } 100 | } 101 | 102 | public var top: CGFloat { 103 | get { 104 | return self.y 105 | } 106 | set(top) { 107 | self.y = top 108 | } 109 | } 110 | 111 | public var centerX: CGFloat { 112 | get { 113 | return self.center.x 114 | } 115 | set(centerX) { 116 | self.center = CGPoint.init( 117 | x: getPixintegral(pointValue: centerX), 118 | y: self.center.y 119 | ) 120 | } 121 | } 122 | 123 | public var centerY: CGFloat { 124 | get { 125 | return self.center.y 126 | } 127 | set (centerY) { 128 | self.center = CGPoint.init(x: self.center.x, y: getPixintegral(pointValue: centerY)) 129 | } 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /PFollow/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPhotoLibraryAddUsageDescription 6 | PFollow 需要获取您的相册读取权限以供您进行读取和保存照片 7 | NSCameraUsageDescription 8 | PFollow 需要获取您的相机运行权限以供您进行拍照 9 | CFBundleDevelopmentRegion 10 | zh_CN 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSLocationWhenInUseUsageDescription 28 | PFollow 需要获取您的地理位置信息以标识大头针和海拔高度 29 | NSMotionUsageDescription 30 | PFollow 需要获取您的健康信息以显示步数 31 | UILaunchStoryboardName 32 | LaunchScreen 33 | UIRequiredDeviceCapabilities 34 | 35 | armv7 36 | 37 | UIStatusBarStyle 38 | UIStatusBarStyleLightContent 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | 43 | UISupportedInterfaceOrientations~ipad 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationPortraitUpsideDown 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /PFollow/PFollow-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 | #import 6 | #import 7 | #import 8 | 9 | -------------------------------------------------------------------------------- /PFollow/PFollow.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.developer.healthkit 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PFollow/PFollow.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | PFollow.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /PFollow/PFollow.xcdatamodeld/PFollow.xcdatamodel/contents: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /PFollow/Page/Home/controller/PJHomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJHomeViewController.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PJHomeViewController: PJBaseViewController, PJHomeBottomViewDelegate, PJMapViewDelete { 12 | 13 | var mapView: PJHomeMapView? 14 | var bottomView: PJHomeBottomView? 15 | var realAnnotations = [AnnotationModel]() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | initView() 20 | } 21 | 22 | override func viewDidAppear(_ animated: Bool) { 23 | super.viewDidAppear(animated) 24 | 25 | if bottomView?.tapBtn.alpha == 0 { 26 | UIView.animate(withDuration: 0.25) { 27 | self.bottomView?.tapBtn.alpha = 1 28 | } 29 | } 30 | } 31 | 32 | override func didReceiveMemoryWarning() { 33 | super.didReceiveMemoryWarning() 34 | } 35 | 36 | 37 | // MARK: life cycle 38 | private func initView() { 39 | view.backgroundColor = .white 40 | 41 | mapView = PJHomeMapView.init(frame: CGRect(x: 0, y: 0, 42 | width: PJSCREEN_WIDTH, 43 | height: PJSCREEN_HEIGHT - PJBottomLinerHeight)) 44 | mapView?.viewDelegate = self 45 | view.addSubview(mapView!) 46 | 47 | bottomView = PJHomeBottomView.init(frame: CGRect(x: -PJSCREEN_WIDTH * 0.1, 48 | y: PJSCREEN_HEIGHT - 120 - PJBottomLinerHeight, 49 | width: PJSCREEN_WIDTH * 1.2, 50 | height: 160 - PJBottomLinerHeight)) 51 | bottomView?.viewDelegate = self 52 | view.addSubview(bottomView!) 53 | 54 | // 视图载入完成后,设置地图缩放等级 55 | mapView?.mapView.setZoomLevel(15, animated: true) 56 | } 57 | 58 | 59 | // MARK: - Action 60 | private func addAnnotationViewToMapView(_ coordinate: CLLocationCoordinate2D, 61 | isLongPress: Bool) { 62 | // 添加新的标记点 63 | self.mapView?.isNewAnnotation = true 64 | 65 | let pointAnnotation = MAPointAnnotation() 66 | pointAnnotation.coordinate = coordinate 67 | self.mapView?.mapView.addAnnotation(pointAnnotation) 68 | } 69 | 70 | 71 | // MARK: delegate 72 | func homeBottomViewPlacesBtnClick(view: PJHomeBottomView) { 73 | UIView.animate(withDuration: 0.25, animations: { 74 | self.bottomView?.tapBtn.alpha = 0 75 | }) { (finished) in 76 | if finished { 77 | let vc = PJPlacesViewController() 78 | vc.annotationModels = self.realAnnotations 79 | let nav = UINavigationController(rootViewController: vc) 80 | self.present(nav, animated: true, completion: nil) 81 | } 82 | } 83 | } 84 | 85 | 86 | func homeBottomViewTapBtnClick(view: PJHomeBottomView, tapBtn: UIButton) { 87 | bottomView?.isRequest = true 88 | UIView.animate(withDuration: 0.15, animations: { 89 | tapBtn.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) 90 | }) { (finished) in 91 | if finished { 92 | UIView.animate(withDuration: 0.15, animations: { 93 | tapBtn.transform = CGAffineTransform(scaleX: 1, y: 1) 94 | }) {(finished) in 95 | if finished { 96 | let point = self.mapView?.mapView.convert((self.mapView?.mapView.userLocation.location.coordinate)!, toPointTo: self.mapView) 97 | print(point as Any) 98 | let tempTapImageView = UIImageView(image: UIImage(named: "home_tap")) 99 | tempTapImageView.frame.size = CGSize(width: 50, height: 50) 100 | tempTapImageView.centerX = view.centerX 101 | tempTapImageView.bottom = (self.bottomView?.bottom)! 102 | self.view.addSubview(tempTapImageView) 103 | UIView.animate(withDuration: 0.25, animations: { 104 | tempTapImageView.center = point! 105 | }) {(finished) in 106 | if finished { 107 | UIView.animate(withDuration: 0.5, animations: { 108 | tempTapImageView.alpha = 0 109 | }, completion: { (finished) in 110 | if finished { 111 | tempTapImageView.removeFromSuperview() 112 | self.addAnnotationViewToMapView((self.mapView?.mapView.userLocation.location.coordinate)!, 113 | isLongPress: false) 114 | } 115 | }) 116 | } 117 | } 118 | } 119 | } 120 | } 121 | } 122 | } 123 | 124 | 125 | func mapViewTappedCalloutView(_ mapView: PJHomeMapView, annotationView: PJHomeMapAnnotationView) { 126 | let vc = PJAnnotationDetailsViewController() 127 | vc.annotationView = annotationView 128 | vc.isHiddenRightBarButton = false 129 | navigationController?.pushViewController(vc, animated: true) 130 | } 131 | 132 | 133 | func mapView(mapView: PJHomeMapView, rotateDegree: CGFloat) { 134 | // 调整指南针选择方向角度 135 | bottomView?.rotateDegree = (rotateDegree - 45) 136 | } 137 | 138 | 139 | func mapView(mapView: PJHomeMapView, isRequested: Bool) { 140 | // 如果失败要给 HUD 提示 141 | if isRequested { 142 | bottomView?.isRequest = !isRequested 143 | } 144 | } 145 | 146 | 147 | func mapViewInitComplate(_ mapView: PJHomeMapView) { 148 | // 读取 Annotation 缓存 149 | let caches = PJCoreDataHelper.shared.allAnnotation() 150 | if caches.count != 0 { 151 | mapView.models = caches 152 | // 从 CoreData 中读取数据 153 | mapView.isCache = true 154 | // 不是新加入的标记点 155 | mapView.isNewAnnotation = false 156 | var index = 0 157 | for annotation in caches { 158 | let pointAnnotation = MAPointAnnotation() 159 | pointAnnotation.coordinate = CLLocationCoordinate2D.init(latitude: Double(annotation.latitude)!, longitude: Double(annotation.longitude)!) 160 | mapView.mapView.addAnnotation(pointAnnotation) 161 | 162 | if annotation.createdTimeString.contains("/") { 163 | realAnnotations.append(annotation) 164 | } 165 | 166 | index += 1 167 | } 168 | } 169 | } 170 | 171 | 172 | func mapView(mapView: PJHomeMapView, didLongPressCoordinate: CLLocationCoordinate2D) { 173 | PJTapic.tap() 174 | addAnnotationViewToMapView(didLongPressCoordinate, isLongPress: true) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /PFollow/Page/Home/model/AnnotationModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnotationModel.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/18. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | struct AnnotationModel: Codable { 13 | // 天气信息 14 | var weatherString: String 15 | // 创建时间 16 | var createdTimeString: String 17 | // 环境信息 18 | var environmentString: String 19 | // 纬度 20 | var latitude: String 21 | // 精度 22 | var longitude: String 23 | // 海拔高度 24 | var altitude: String 25 | // 步数 26 | var stepCount: String 27 | // 城市 28 | var city: String 29 | // 格式化地址 30 | var formatterAddress: String 31 | // 大头针图片名字 32 | var markerName: String 33 | } 34 | -------------------------------------------------------------------------------- /PFollow/Page/Home/view/MapView/PJHomeMapAnnotetionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJHomeMapAnnotetionView.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/16. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol PJHomeMapAnnotationViewDelegate { 12 | @objc optional func homeMapAnnotationView(annotationView: PJHomeMapAnnotationView, removeAnnotaion: MAAnnotation) 13 | @objc optional func homeMapAnnotationView(annotationView: PJHomeMapAnnotationView, shareAnnotaion: MAAnnotation) 14 | @objc optional func homeMapAnnotationViewTappedView(calloutView: PJHomeMapCalloutView, annotationView: PJHomeMapAnnotationView) 15 | } 16 | 17 | class PJHomeMapAnnotationView: MAAnnotationView, PJHomeMapCalloutViewDelegate, CAAnimationDelegate { 18 | 19 | var viewDelegate: PJHomeMapAnnotationViewDelegate? 20 | var model: AnnotationModel? 21 | 22 | private var calloutView: PJHomeMapCalloutView? 23 | private var kCalloutWidth = 180.0 24 | private var kCalloutHeight = 100.0 25 | 26 | 27 | override init!(annotation: MAAnnotation!, reuseIdentifier: String!) { 28 | super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) 29 | 30 | NotificationCenter.default.addObserver(self, 31 | selector: #selector(updateCalloutView), 32 | name: NSNotification.Name(rawValue: PJNotificationName_updateCallouView), 33 | object: nil) 34 | } 35 | 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | model = nil 39 | super.init(coder: aDecoder) 40 | } 41 | 42 | 43 | // MARK: override 44 | override func setSelected(_ selected: Bool, animated: Bool) { 45 | if self.isSelected == selected { 46 | return 47 | } 48 | if selected { 49 | if calloutView == nil { 50 | calloutView = PJHomeMapCalloutView(frame: CGRect(x: 0, y: 0, width: kCalloutWidth, height: kCalloutHeight)) 51 | calloutView?.center = CGPoint(x: bounds.width / 2.0 + calloutOffset.x, y: -((calloutView?.bounds.height)! / 2.0) + calloutOffset.y) 52 | } 53 | calloutView?.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) 54 | calloutView?.viewDelegate = self 55 | calloutView?.y += 20 56 | calloutView?.alpha = 0 57 | 58 | if model != nil { 59 | calloutView?.model = model 60 | } 61 | 62 | UIView.animateKeyframes(withDuration: 0.15, delay: 0, options: .calculationModeCubic, animations: { 63 | self.calloutView?.alpha = 1.0 64 | self.calloutView?.y -= 20 65 | self.calloutView?.transform = CGAffineTransform(scaleX: 1, y: 1) 66 | }) { (finished) in 67 | PJTapic.tipsTap() 68 | } 69 | 70 | addSubview(calloutView!) 71 | } else { 72 | calloutView?.removeFromSuperview() 73 | } 74 | 75 | super.setSelected(selected, animated: animated) 76 | } 77 | 78 | 79 | // 修改响应链 80 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 81 | if calloutView != nil { 82 | let deletePoint = calloutView?.deleteBtn.convert(point, from: self) 83 | if (calloutView?.deleteBtn.bounds.contains(deletePoint!))! { 84 | return calloutView?.deleteBtn 85 | } 86 | let sharePoint = calloutView?.shareBtn.convert(point, from: self) 87 | if (calloutView?.shareBtn.bounds.contains(sharePoint!))! { 88 | return calloutView?.shareBtn 89 | } 90 | let calloutPoint = calloutView?.convert(point, from: self) 91 | if (calloutView?.bounds.contains(calloutPoint!))! { 92 | return calloutView 93 | } 94 | } 95 | return super.hitTest(point, with: event) 96 | } 97 | 98 | 99 | override func willMove(toSuperview newSuperview: UIView?) { 100 | super.willMove(toSuperview: newSuperview) 101 | 102 | // 防止删除大头针的时候调用该方法,传入 nil 103 | if newSuperview == nil { 104 | return 105 | } 106 | 107 | if(newSuperview?.bounds.contains(self.center))! { 108 | let growAnimation = CABasicAnimation.init(keyPath: "transform.scale") 109 | growAnimation.delegate = self 110 | growAnimation.duration = 0.25; 111 | growAnimation.timingFunction = CAMediaTimingFunction.init(name: kCAMediaTimingFunctionLinear) 112 | growAnimation.fromValue = 0 113 | growAnimation.toValue = 1.0 114 | 115 | self.layer.add(growAnimation, forKey: "growAnimation") 116 | } 117 | } 118 | 119 | 120 | // MARK: Notification 121 | @objc private func updateCalloutView(notifi: Notification) { 122 | let timeString = notifi.userInfo!["time"] as! String 123 | calloutView?.titleString = timeString 124 | } 125 | 126 | 127 | // MARK: delegate 128 | func homeMapCalloutRemoveAnnotation(callout: PJHomeMapCalloutView) { 129 | viewDelegate?.homeMapAnnotationView!(annotationView: self, removeAnnotaion: self.annotation) 130 | } 131 | 132 | 133 | func homeMapCalloutShareAnnotation(callout: PJHomeMapCalloutView) { 134 | viewDelegate?.homeMapAnnotationView!(annotationView: self, shareAnnotaion: self.annotation) 135 | } 136 | 137 | 138 | func homeMapCalloutTapped(callout: PJHomeMapCalloutView) { 139 | viewDelegate?.homeMapAnnotationViewTappedView!(calloutView: callout, annotationView: self) 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /PFollow/Page/Home/view/MapView/PJHomeMapCalloutView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJHomeMapCalloutView.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/14. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @objc protocol PJHomeMapCalloutViewDelegate { 12 | @objc optional func homeMapCalloutRemoveAnnotation(callout: PJHomeMapCalloutView) 13 | @objc optional func homeMapCalloutShareAnnotation(callout: PJHomeMapCalloutView) 14 | @objc optional func homeMapCalloutTapped(callout: PJHomeMapCalloutView) 15 | } 16 | 17 | class PJHomeMapCalloutView: UIView { 18 | 19 | var viewDelegate: PJHomeMapCalloutViewDelegate? 20 | var model: AnnotationModel? { 21 | willSet(model) { 22 | willSetModel(model!) 23 | } 24 | } 25 | 26 | var titleString: String = "" { 27 | willSet(s) { 28 | willSetTitleString(s) 29 | } 30 | } 31 | 32 | private(set) var shareBtn = UIButton() 33 | private(set) var deleteBtn = UIButton() 34 | private var weatherImageView = UIImageView() 35 | private var titleLabel = UILabel() 36 | private var temperatureLabel = UILabel() 37 | private let kArrorHeight = CGFloat(10) 38 | 39 | override init(frame: CGRect) { 40 | super.init(frame: frame) 41 | self.backgroundColor = .clear 42 | self.isUserInteractionEnabled = true 43 | let tapped = UITapGestureRecognizer.init(target: self, action: #selector(calloutViewTapped)) 44 | self.addGestureRecognizer(tapped) 45 | 46 | weatherImageView.frame = CGRect(x: 0, y: 0, width: 20, height: 20) 47 | addSubview(weatherImageView) 48 | 49 | temperatureLabel.textColor = .black 50 | temperatureLabel.textAlignment = .left 51 | temperatureLabel.font = UIFont.systemFont(ofSize: 12, weight: .thin) 52 | temperatureLabel.frame = CGRect(x: 22, y: weatherImageView.y, width: 30, height: 20) 53 | addSubview(temperatureLabel) 54 | 55 | titleLabel.textColor = .black 56 | titleLabel.textAlignment = .center 57 | titleLabel.font = UIFont.systemFont(ofSize: 16, weight: .regular) 58 | titleLabel.frame = CGRect(x: 5, y: temperatureLabel.bottom + 10, width: self.width - 10, height: 20) 59 | addSubview(titleLabel) 60 | 61 | let stackView = UIStackView(frame: CGRect(x: 0, y: self.bottom - 45, width: self.width, height: 30)) 62 | stackView.alignment = .fill 63 | stackView.axis = .horizontal 64 | stackView.spacing = 10 65 | stackView.distribution = .fillEqually 66 | addSubview(stackView) 67 | 68 | deleteBtn.addTarget(self, action: #selector(deleteBtnAction), for: .touchUpInside) 69 | deleteBtn.setImage(UIImage(named: "callout_delete"), for: .normal) 70 | stackView.addArrangedSubview(deleteBtn) 71 | 72 | shareBtn.addTarget(self, action: #selector(shareBtnAction), for: .touchUpInside) 73 | shareBtn.setImage(UIImage(named: "callout_share"), for: .normal) 74 | stackView.addArrangedSubview(shareBtn) 75 | } 76 | 77 | required init?(coder aDecoder: NSCoder) { 78 | super.init(coder: aDecoder) 79 | } 80 | 81 | 82 | // MARK: Action 83 | @objc private func deleteBtnAction() { 84 | viewDelegate?.homeMapCalloutRemoveAnnotation!(callout: self) 85 | } 86 | 87 | 88 | @objc private func shareBtnAction() { 89 | viewDelegate?.homeMapCalloutShareAnnotation!(callout: self) 90 | } 91 | 92 | 93 | @objc private func calloutViewTapped() { 94 | viewDelegate?.homeMapCalloutTapped!(callout: self) 95 | } 96 | 97 | 98 | private func willSetTitleString(_ s: String) { 99 | titleLabel.text = s 100 | } 101 | 102 | 103 | override func draw(_ rect: CGRect) { 104 | if let aContext = UIGraphicsGetCurrentContext() { 105 | draw(in: aContext) 106 | } 107 | layer.shadowColor = UIColor.black.cgColor 108 | layer.shadowOpacity = 0.3 109 | layer.shadowOffset = CGSize(width: 0.0, height: 2.0) 110 | } 111 | 112 | func draw(in context: CGContext) { 113 | context.setLineWidth(2.0) 114 | context.setFillColor(UIColor.white.cgColor) 115 | getDrawPath(context) 116 | context.fillPath() 117 | } 118 | 119 | func getDrawPath(_ context: CGContext?) { 120 | let rrect: CGRect = bounds 121 | let radius: CGFloat = 6.0 122 | let minx = rrect.minX 123 | let midx = rrect.midX 124 | let maxx = rrect.maxX 125 | let miny = rrect.minY 126 | let maxy: CGFloat = rrect.maxY - kArrorHeight 127 | context?.move(to: CGPoint(x: midx + kArrorHeight, y: maxy)) 128 | context?.addLine(to: CGPoint(x: midx, y: maxy + kArrorHeight)) 129 | context?.addLine(to: CGPoint(x: midx - kArrorHeight, y: maxy)) 130 | context?.addArc(tangent1End: CGPoint(x: minx, y: maxy), tangent2End: CGPoint(x: minx, y: miny), radius: radius) 131 | context?.addArc(tangent1End: CGPoint(x: minx, y: minx), tangent2End: CGPoint(x: maxx, y: miny), radius: radius) 132 | context?.addArc(tangent1End: CGPoint(x: maxx, y: miny), tangent2End: CGPoint(x: maxx, y: maxx), radius: radius) 133 | context?.addArc(tangent1End: CGPoint(x: maxx, y: maxy), tangent2End: CGPoint(x: midx, y: maxy), radius: radius) 134 | context?.closePath() 135 | } 136 | 137 | 138 | // MARK: setter and getter 139 | private func willSetModel(_ model: AnnotationModel) { 140 | if model.createdTimeString.contains("/") { 141 | let startIndex = model.createdTimeString.startIndex 142 | let endIndex = model.createdTimeString.index(startIndex, offsetBy: 10) 143 | titleLabel.text = String(model.createdTimeString[startIndex...endIndex]) + " 来过" 144 | } else { 145 | titleLabel.text = model.createdTimeString 146 | } 147 | 148 | weatherImageView.image = UIImage(named: model.weatherString) 149 | temperatureLabel.text = model.environmentString 150 | temperatureLabel.sizeToFit() 151 | temperatureLabel.left = weatherImageView.right + 2 152 | temperatureLabel.centerY = weatherImageView.centerY + 2 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /PFollow/Page/Home/view/MapView/PJHomeMapView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJHomeMapView.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreMotion 11 | 12 | protocol PJMapViewDelete: class { 13 | func mapView(mapView: PJHomeMapView, 14 | rotateDegree: CGFloat) 15 | func mapView(mapView: PJHomeMapView, 16 | isRequested: Bool) 17 | func mapView(mapView: PJHomeMapView, 18 | didLongPressCoordinate: CLLocationCoordinate2D) 19 | func mapViewInitComplate(_ mapView: PJHomeMapView) 20 | func mapViewTappedCalloutView(_ mapView: PJHomeMapView, 21 | annotationView: PJHomeMapAnnotationView) 22 | } 23 | extension PJMapViewDelete { 24 | func mapView(mapView: PJHomeMapView, 25 | rotateDegree: CGFloat) {} 26 | func mapView(mapView: PJHomeMapView, 27 | isRequested: Bool) {} 28 | func mapView(mapView: PJHomeMapView, 29 | didLongPressCoordinate: CLLocationCoordinate2D) {} 30 | func mapViewInitComplate(_ mapView: PJHomeMapView) {} 31 | func mapViewTappedCalloutView(_ mapView: PJHomeMapView, 32 | annotationView: PJHomeMapAnnotationView) {} 33 | } 34 | 35 | class PJHomeMapView: UIView, MAMapViewDelegate, AMapSearchDelegate, PJHomeMapAnnotationViewDelegate { 36 | 37 | static let PJNotificationName_annotation = Notification.Name("PJNotificationName_annotation") 38 | 39 | weak var viewDelegate: PJMapViewDelete? 40 | var models = [AnnotationModel]() 41 | 42 | // 是否从 CoreData 中读取数据 43 | var isCache = false 44 | // 是否为新建标记点 45 | var isNewAnnotation = true 46 | // 是否为用户长按添加的标记点 47 | private var isLongPress = false 48 | 49 | private var isBigZoom = false 50 | private var isSmallZoom = false 51 | private var notificationRecoder = 0 52 | private var currentCacheAnnotationIndex = 0 53 | 54 | private var currentAnnotation: MAAnnotation? 55 | private var currentAnnotationModel: AnnotationModel? 56 | private var currentAnnotationView: PJHomeMapAnnotationView? 57 | 58 | private(set) var mapView: MAMapView = MAMapView() 59 | 60 | private var r = MAUserLocationRepresentation() 61 | private var pedometer = CMPedometer() 62 | private let search = AMapSearchAPI() 63 | private let req = AMapWeatherSearchRequest() 64 | // 查询完环境和天气后的最终字典 65 | private var finalModelDict = [String: String]() 66 | // 地图上的当前所有大头针 67 | private var annotationViews = [PJHomeMapAnnotationView]() 68 | private var biggerAnnotationViews = [PJHomeMapAnnotationView]() 69 | private var smallAnnotationViews = [PJHomeMapAnnotationView]() 70 | 71 | 72 | // MARK: life cycle 73 | override init(frame: CGRect) { 74 | super.init(frame: frame) 75 | initView() 76 | 77 | NotificationCenter.default.addObserver(self, 78 | selector: #selector(messageQueueNotification(notify:)), 79 | name: PJHomeMapView.PJNotificationName_annotation, 80 | object: nil) 81 | } 82 | 83 | 84 | required init?(coder aDecoder: NSCoder) { 85 | super.init(coder: aDecoder) 86 | } 87 | 88 | 89 | fileprivate func initView() { 90 | AMapServices.shared().enableHTTPS = true 91 | 92 | search?.delegate = self 93 | req.type = AMapWeatherType.live 94 | 95 | mapView.frame = frame 96 | mapView.delegate = self 97 | // 设置比例尺原点位置 98 | mapView.scaleOrigin = CGPoint(x: 10, y: 30) 99 | // 设置罗盘原点位置 100 | mapView.compassOrigin = CGPoint(x: PJSCREEN_WIDTH - 50, y: 30) 101 | // 开启地图自定义样式 102 | mapView.customMapStyleEnabled = true; 103 | // 显示显示用户位置 104 | mapView.showsUserLocation = true 105 | // 用户模式跟踪 106 | mapView.userTrackingMode = .follow 107 | addSubview(mapView) 108 | 109 | r.image = UIImage(named: "home_map_userlocation") 110 | r.showsAccuracyRing = false 111 | mapView.update(r) 112 | 113 | var path = Bundle.main.bundlePath 114 | path.append("/mapView.data") 115 | let jsonData = NSData.init(contentsOfFile: path) 116 | mapView.setCustomMapStyleWithWebData(jsonData as Data?) 117 | } 118 | 119 | 120 | // MARK: notification 121 | @objc private func messageQueueNotification(notify: Notification) { 122 | var params = notify.userInfo as! [String: String] 123 | if params["notifi_name"] == "city" { 124 | notificationRecoder += 1 125 | params.removeValue(forKey: "notifi_name") 126 | finalModelDict.merge(params, 127 | uniquingKeysWith: { $1 }) 128 | } 129 | 130 | if params["notifi_name"] == "weather" { 131 | params.removeValue(forKey: "notifi_name") 132 | notificationRecoder += 1 133 | finalModelDict.merge(params, 134 | uniquingKeysWith: { $1 }) 135 | } 136 | 137 | if notificationRecoder == 2 { 138 | notificationRecoder = 0 139 | getPedonmeterData(json: finalModelDict) 140 | } 141 | 142 | 143 | if isLongPress { 144 | notificationRecoder = 0 145 | isLongPress = false 146 | 147 | let longPressModel = AnnotationModel(weatherString: "-", 148 | createdTimeString: "还未填写来过时间", 149 | environmentString: "-", 150 | latitude: String(Double(currentAnnotation!.coordinate.latitude)), 151 | longitude: String(Double(currentAnnotation!.coordinate.longitude)), 152 | altitude: "-", 153 | stepCount: "-", 154 | city: finalModelDict["city"]!, 155 | formatterAddress: finalModelDict["formatterAddress"]!, 156 | markerName: "home_map_makers_03") 157 | 158 | currentAnnotationView?.model = longPressModel 159 | // models 为 controller 传入,更新 annotationView 时需要用到它,所以当在 mapview 内部添加新 model 时,需要更新 models 160 | models.append(longPressModel) 161 | 162 | if mapView.zoomLevel < 12.8 { 163 | currentAnnotationView?.image = UIImage(named: longPressModel.markerName) 164 | } else { 165 | currentAnnotationView?.image = UIImage(named: longPressModel.markerName + "_b") 166 | } 167 | let _ = addNewAnnotationView(annotationModel: longPressModel, 168 | annotationView: currentAnnotationView!) 169 | } 170 | } 171 | 172 | 173 | private func getPedonmeterData(json: [String: String]) { 174 | pedometer = CMPedometer() 175 | if CMPedometer.isStepCountingAvailable(){ 176 | let calendar = Calendar.current 177 | let now = Date() 178 | let components = calendar.dateComponents([.year, .month, .day], 179 | from: now) 180 | let startDate = calendar.date(from: components) 181 | pedometer.queryPedometerData(from: startDate!, to: Date(), withHandler: { (data, error) in 182 | if error != nil{ 183 | print("/(error?.localizedDescription)") 184 | }else{ 185 | if data != nil { 186 | var json = json 187 | json["stepCount"] = String(Int(truncating: (data?.numberOfSteps)!)) 188 | if let json = try? JSONSerialization.data(withJSONObject: json, 189 | options: []) { 190 | if let annotationModel = try? JSONDecoder().decode(AnnotationModel.self, 191 | from: json) { 192 | DispatchQueue.main.async { 193 | self.currentAnnotationView?.model = annotationModel 194 | 195 | if self.mapView.zoomLevel < 12.8 { 196 | self.currentAnnotationView?.image = UIImage(named: annotationModel.markerName) 197 | } else { 198 | self.currentAnnotationView?.image = UIImage(named: annotationModel.markerName + "_b") 199 | } 200 | 201 | self.models.append(annotationModel) 202 | // 添加新大头针 203 | let isRequest = self.addNewAnnotationView(annotationModel: annotationModel, 204 | annotationView: self.currentAnnotationView!) 205 | self.viewDelegate?.mapView(mapView: self, 206 | isRequested: isRequest) 207 | } 208 | } 209 | } 210 | } 211 | } 212 | }) 213 | } 214 | } 215 | 216 | 217 | // tag == 0 为用户位置蓝点,在添加上 mapView 之前先判断然后不允许进行交互 218 | func mapView(_ mapView: MAMapView!, didAddAnnotationViews views: [Any]!) { 219 | for view in views { 220 | let v = view as! MAAnnotationView 221 | if v.tag == 0 { 222 | v.isUserInteractionEnabled = false 223 | } 224 | } 225 | } 226 | 227 | 228 | func mapView(_ mapView: MAMapView!, 229 | viewFor annotation: MAAnnotation!) -> MAAnnotationView! { 230 | // 若为用户标签则 nil 231 | if annotation.isKind(of: MAUserLocation.self) { 232 | return nil 233 | } 234 | 235 | // 判断如果是 `MAPointAnnotation` 类型则返回自定义大头针 236 | if annotation.isKind(of: MAPointAnnotation.self) { 237 | let annotationStyleReuseIndetifier = "annotationStyleReuserIdentifier" 238 | 239 | var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationStyleReuseIndetifier) as! PJHomeMapAnnotationView? 240 | 241 | if annotationView == nil { 242 | annotationView = PJHomeMapAnnotationView(annotation: annotation, 243 | reuseIdentifier: annotationStyleReuseIndetifier) 244 | } 245 | annotationView?.canShowCallout = false 246 | annotationView?.viewDelegate = self 247 | // 该 tag 只是用于跟 userLocal 标记分开,不能唯一标识一个大头针 248 | annotationView?.tag = -2333 249 | 250 | currentAnnotation = annotation 251 | currentAnnotationView = annotationView 252 | 253 | if isCache && !isNewAnnotation { 254 | for model in models { 255 | if model.latitude == String(Double(annotation.coordinate.latitude)) && 256 | model.longitude == String(Double(annotation.coordinate.longitude)) { 257 | annotationView?.model = model 258 | if mapView.zoomLevel < 12.8 { 259 | annotationView?.image = UIImage(named: model.markerName) 260 | } else { 261 | annotationView?.image = UIImage(named: model.markerName + "_b") 262 | } 263 | 264 | annotationViews.append(annotationView!) 265 | break 266 | } 267 | } 268 | return annotationView 269 | } 270 | 271 | // 请求环境数据 272 | let request = AMapReGeocodeSearchRequest() 273 | request.location = AMapGeoPoint.location(withLatitude: CGFloat(annotation.coordinate.latitude), 274 | longitude: CGFloat(annotation.coordinate.longitude)) 275 | request.requireExtension = true 276 | search?.aMapReGoecodeSearch(request) 277 | 278 | // 添加完新的标记点后,设置为 false 279 | isNewAnnotation = false 280 | return annotationView 281 | } 282 | 283 | return nil 284 | } 285 | 286 | 287 | func addNewAnnotationView(annotationModel: AnnotationModel, 288 | annotationView: PJHomeMapAnnotationView) -> Bool { 289 | self.annotationViews.append(annotationView) 290 | let isSaved = PJCoreDataHelper.shared.addAnnotation(model: annotationModel) 291 | return isSaved 292 | } 293 | 294 | 295 | // MARK:delegate 296 | func homeMapAnnotationView(annotationView: PJHomeMapAnnotationView, 297 | shareAnnotaion: MAAnnotation) { 298 | 299 | } 300 | 301 | 302 | func homeMapAnnotationView(annotationView: PJHomeMapAnnotationView, 303 | removeAnnotaion: MAAnnotation) { 304 | PJCoreDataHelper.shared.deleteAnnotation(model: annotationView.model!) 305 | 306 | UIView.animate(withDuration: 0.2, animations: { 307 | annotationView.y -= 15 308 | }) { (finished) in 309 | if finished { 310 | UIView.animate(withDuration: 0.2, animations: { 311 | annotationView.y += 20 312 | }, completion: { (finished) in 313 | if finished { 314 | UIView.animate(withDuration: 0.25, animations: { 315 | annotationView.y -= 5 316 | }, completion: { (finished) in 317 | if finished { 318 | UIView.animate(withDuration: 0.25, animations: { 319 | annotationView.alpha = 0 320 | }, completion: { (finished) in 321 | if finished { 322 | self.mapView.removeAnnotation(removeAnnotaion) 323 | var index = 0 324 | for annotation in self.annotationViews { 325 | if Double(annotation.model!.latitude) == removeAnnotaion.coordinate.latitude && 326 | Double(annotation.model!.longitude) == removeAnnotaion.coordinate.longitude { 327 | self.annotationViews.remove(at: index) 328 | // 删除完要退出。😂 329 | return 330 | } 331 | index += 1 332 | } 333 | } 334 | }) 335 | } 336 | }) 337 | } 338 | }) 339 | } 340 | } 341 | } 342 | 343 | 344 | func homeMapAnnotationViewTappedView(calloutView: PJHomeMapCalloutView, 345 | annotationView: PJHomeMapAnnotationView) { 346 | viewDelegate?.mapViewTappedCalloutView(self, annotationView: annotationView) 347 | } 348 | 349 | 350 | func mapView(_ mapView: MAMapView!, didUpdate userLocation: MAUserLocation!, 351 | updatingLocation: Bool) { 352 | if !updatingLocation { 353 | viewDelegate?.mapView(mapView: self, 354 | rotateDegree: CGFloat(userLocation.heading.trueHeading) - mapView.rotationDegree) 355 | } 356 | } 357 | 358 | 359 | func onReGeocodeSearchDone(_ request: AMapReGeocodeSearchRequest!, 360 | response: AMapReGeocodeSearchResponse!) { 361 | if response.regeocode != nil { 362 | // 请求天气数据,如果是长按添加的大头针则不请求 363 | if !isLongPress { 364 | req.city = response.regeocode.addressComponent.city 365 | search?.aMapWeatherSearch(req) 366 | } 367 | 368 | let params: [String: String] = [ 369 | "notifi_name": "city", 370 | 371 | "city": response.regeocode.addressComponent.city, 372 | "formatterAddress": response.regeocode.formattedAddress, 373 | "markerName": "home_map_makers_02", 374 | ] 375 | NotificationCenter.default.post(name: PJHomeMapView.PJNotificationName_annotation, 376 | object: nil, 377 | userInfo: params) 378 | 379 | print(response.regeocode.addressComponent.city) 380 | print(response.regeocode.addressComponent.citycode) 381 | } 382 | } 383 | 384 | 385 | func onWeatherSearchDone(_ request: AMapWeatherSearchRequest!, 386 | response: AMapWeatherSearchResponse!) { 387 | let environmentString = response.lives[0].temperature + "° " + 388 | response.lives[0].windDirection + "风" + 389 | response.lives[0].windPower + "级 " + 390 | response.lives[0].humidity + "%rh" 391 | 392 | let timeFormatter = DateFormatter() 393 | timeFormatter.dateFormat = "yyyy/MM/dd HH:mm" 394 | 395 | 396 | let params: [String: String] = [ 397 | "notifi_name": "weather", 398 | 399 | "createdTimeString": timeFormatter.string(from: Date()) as String + " 来过", 400 | "weatherString": response.lives[0].weather, 401 | "environmentString": environmentString, 402 | "latitude": String(Double((currentAnnotation?.coordinate.latitude)!)), 403 | "longitude": String(Double((currentAnnotation?.coordinate.longitude)!)), 404 | "altitude": String(Int(mapView.userLocation.location.altitude)) 405 | ] 406 | NotificationCenter.default.post(name: PJHomeMapView.PJNotificationName_annotation, 407 | object: nil, 408 | userInfo: params) 409 | } 410 | 411 | 412 | func mapView(_ mapView: MAMapView!, mapDidZoomByUser wasUserAction: Bool) { 413 | 414 | func changeAnnotation() { 415 | let annotationSet = mapView.annotations(in: mapView.visibleMapRect).filter { (item) in 416 | !(item is MAUserLocation) } as! Set 417 | 418 | mapView.removeAnnotations(Array(annotationSet)) 419 | 420 | for annotation in annotationSet { 421 | let pointAnnotation = MAPointAnnotation() 422 | pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: annotation.coordinate.latitude, 423 | longitude: annotation.coordinate.longitude) 424 | mapView.addAnnotation(pointAnnotation) 425 | } 426 | } 427 | 428 | if mapView.zoomLevel <= 12.8 { 429 | if isSmallZoom == false { 430 | isSmallZoom = true 431 | isBigZoom = false 432 | 433 | changeAnnotation() 434 | } 435 | } else { 436 | if isBigZoom == false { 437 | isBigZoom = true 438 | isSmallZoom = false 439 | 440 | changeAnnotation() 441 | } 442 | } 443 | } 444 | 445 | 446 | func mapView(_ mapView: MAMapView!, mapDidMoveByUser wasUserAction: Bool) { 447 | guard annotationViews.count != 0 else { 448 | return 449 | } 450 | 451 | let annotationArray = Array(mapView.annotations(in: mapView.visibleMapRect).filter { (item) in 452 | !(item is MAUserLocation) } as! Set) 453 | 454 | let screenAnntationView = annotationViews.filter { (item) in 455 | for annotation in annotationArray { 456 | if annotation.coordinate.latitude == Double(item.model!.latitude) && 457 | annotation.coordinate.longitude == Double(item.model!.longitude) { 458 | return true 459 | } 460 | } 461 | return false 462 | } 463 | 464 | 465 | for annotationView in screenAnntationView { 466 | if mapView.zoomLevel < 12.8 { 467 | annotationView.image = UIImage(named: annotationView.model!.markerName) 468 | } else { 469 | annotationView.image = UIImage(named: annotationView.model!.markerName + "_b") 470 | } 471 | } 472 | } 473 | 474 | 475 | func mapView(_ mapView: MAMapView!, didLongPressedAt coordinate: CLLocationCoordinate2D) { 476 | isLongPress = true 477 | viewDelegate?.mapView(mapView: self, didLongPressCoordinate: coordinate) 478 | } 479 | 480 | 481 | func aMapSearchRequest(_ request: Any!, didFailWithError error: Error!) { 482 | print("Error:\(error)") 483 | } 484 | 485 | 486 | func mapInitComplete(_ mapView: MAMapView!) { 487 | viewDelegate?.mapViewInitComplate(self) 488 | } 489 | 490 | 491 | } 492 | -------------------------------------------------------------------------------- /PFollow/Page/Home/view/bottomView/PJHomeBottomView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJHomeBottomView.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol PJHomeBottomViewDelegate: class { 12 | func homeBottomViewPlacesBtnClick(view: PJHomeBottomView) 13 | func homeBottomViewTapBtnClick(view: PJHomeBottomView, tapBtn: UIButton) 14 | } 15 | extension PJHomeBottomViewDelegate { 16 | func homeBottomViewPlacesBtnClick(view: PJHomeBottomView) {} 17 | func homeBottomViewTapBtnClick(view: PJHomeBottomView, tapBtn: UIButton) {} 18 | } 19 | 20 | class PJHomeBottomView: UIView { 21 | 22 | weak var viewDelegate: PJHomeBottomViewDelegate? 23 | var rotateDegree:CGFloat{ 24 | set { 25 | self.compassImageView.transform = CGAffineTransform(rotationAngle: newValue * .pi / 180.0) 26 | } 27 | get { 28 | return self.rotateDegree 29 | } 30 | } 31 | 32 | var isRequest: Bool { 33 | willSet(b) { 34 | willSetIsRequest(b) 35 | } 36 | } 37 | 38 | private(set) var tapBtn = UIButton() 39 | private(set) var addAnnotationOKImageView = UIImageView() 40 | 41 | private var compassImageView = UIImageView() 42 | private var indicator = UIActivityIndicatorView(activityIndicatorStyle: .gray) 43 | 44 | // MARK: life cycle 45 | override init(frame: CGRect) { 46 | isRequest = false 47 | super.init(frame: frame) 48 | initView() 49 | } 50 | 51 | 52 | required init?(coder aDecoder: NSCoder) { 53 | isRequest = false 54 | super.init(coder: aDecoder) 55 | } 56 | 57 | 58 | private func initView() { 59 | let backView = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: 160)) 60 | addSubview(backView) 61 | backView.isUserInteractionEnabled = true 62 | backView.image = UIImage(named: "home_cloud") 63 | 64 | let tapped = UITapGestureRecognizer(target: self, action: #selector(placesBtnClick)) 65 | backView.addGestureRecognizer(tapped) 66 | 67 | tapBtn.width = 70 68 | tapBtn.height = 70 69 | // 0.1 为 cloud 的放大偏移量 70 | tapBtn.centerX = self.centerX + PJSCREEN_WIDTH * 0.1 71 | tapBtn.y = (height - 70) / 2 72 | tapBtn.layer.shadowColor = UIColor.black.cgColor 73 | tapBtn.layer.shadowRadius = 5 74 | tapBtn.layer.shadowOpacity = 0.3 75 | tapBtn.layer.shadowOffset = CGSize(width: 0, height: 2) 76 | tapBtn.setImage(UIImage(named: "home_tap"), for: .normal) 77 | tapBtn.addTarget(self, action: #selector(tapBtnClick(sender:)), for: .touchUpInside) 78 | addSubview(tapBtn) 79 | 80 | compassImageView.width = 30 81 | compassImageView.height = 30 82 | compassImageView.center = tapBtn.center 83 | compassImageView.image = UIImage(named: "home_bottom_compass") 84 | addSubview(compassImageView) 85 | 86 | addAnnotationOKImageView.width = 30 87 | addAnnotationOKImageView.height = 30 88 | addAnnotationOKImageView.center = tapBtn.center 89 | addAnnotationOKImageView.image = UIImage(named: "home_tap_ok") 90 | addSubview(addAnnotationOKImageView) 91 | addAnnotationOKImageView.isHidden = true 92 | addAnnotationOKImageView.alpha = 0 93 | 94 | indicator.width = 30 95 | indicator.height = 30 96 | indicator.center = tapBtn.center 97 | addSubview(indicator) 98 | indicator.isHidden = true 99 | } 100 | 101 | 102 | // MARK: Action 103 | @objc private func tapBtnClick(sender: UIButton) { 104 | print("tap") 105 | viewDelegate?.homeBottomViewTapBtnClick(view: self, tapBtn: sender) 106 | } 107 | 108 | @objc private func placesBtnClick() { 109 | print("places") 110 | viewDelegate?.homeBottomViewPlacesBtnClick(view: self) 111 | } 112 | 113 | @objc private func friendBtnClick() { 114 | print("friend") 115 | } 116 | 117 | 118 | private func willSetIsRequest(_ b: Bool) { 119 | if b { 120 | tapBtn.isEnabled = false 121 | compassImageView.isHidden = true 122 | indicator.isHidden = false 123 | indicator.startAnimating() 124 | } else { 125 | indicator.isHidden = true 126 | indicator.stopAnimating() 127 | addAnnotationOKImageView.isHidden = false 128 | UIView.animate(withDuration: 0.25, animations: { 129 | self.addAnnotationOKImageView.alpha = 1.0 130 | self.addAnnotationOKImageView.transform = CGAffineTransform(scaleX: 1.3, y: 1.3) 131 | PJTapic.tap() 132 | }) { (finished) in 133 | self.addAnnotationOKImageView.transform = CGAffineTransform(scaleX: 1, y: 1) 134 | PJTapic.tipsTap() 135 | 136 | UIView.animate(withDuration: 0.25, delay: 0.5, options: .curveEaseIn , animations: { 137 | self.addAnnotationOKImageView.alpha = 0 138 | }, completion: { (finished) in 139 | if finished { 140 | self.addAnnotationOKImageView.isHidden = true 141 | self.compassImageView.isHidden = false 142 | self.tapBtn.isEnabled = true 143 | } 144 | }) 145 | } 146 | } 147 | } 148 | 149 | } 150 | -------------------------------------------------------------------------------- /PFollow/Page/InfoFlow/controller/Details/Controller/PJAnnotationDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJAnnotationDetailsViewController.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/19. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PJAnnotationDetailsViewController: PJBaseViewController, UIScrollViewDelegate, UITextViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, PJDatePickerViewDelegate { 12 | 13 | var annotationView: PJHomeMapAnnotationView? 14 | var isHiddenRightBarButton: Bool? 15 | 16 | private var environmentLabel: UILabel? 17 | private var healthLabel: UILabel? 18 | private var locationLabel: UILabel? 19 | private var envImageView: UIImageView? 20 | private var heaImageView: UIImageView? 21 | private var contentTextView: UITextView? 22 | private var addTimeButton: UIButton? 23 | 24 | private var photoContentView: UIView? 25 | private var addPhotoButton: UIButton? 26 | private var contentTextViewTipsLabel: UILabel? 27 | private var backScrollView: UIScrollView? 28 | private var newPhotoImage: UIImage? 29 | 30 | private var contentTextViewLeftPadding: CGFloat? 31 | 32 | private var previousContentText: String? 33 | private var updateTimeString: String? 34 | 35 | 36 | 37 | // MARK: - life cycle 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | 41 | leftBarButtonItemAction(action: #selector(leftBarButtonTapped)) 42 | view.backgroundColor = PJRGB(r: 31, g: 31, b: 31) 43 | 44 | initView() 45 | initData() 46 | } 47 | 48 | override func didReceiveMemoryWarning() { 49 | super.didReceiveMemoryWarning() 50 | } 51 | 52 | 53 | fileprivate func initView() { 54 | backScrollView = UIScrollView(frame: CGRect(x: 0, y: headerView!.bottom, 55 | width: view.width, height: view.height)) 56 | backScrollView?.delegate = self 57 | backScrollView?.showsVerticalScrollIndicator = false 58 | view.addSubview(backScrollView!) 59 | 60 | 61 | locationLabel = UILabel(frame: CGRect(x: 10, y: 0, 62 | width: view.width - 20, height: navBarHeigt!)) 63 | locationLabel?.font = UIFont.boldSystemFont(ofSize: 22) 64 | locationLabel?.textColor = .white 65 | locationLabel?.numberOfLines = 2 66 | backScrollView?.addSubview(locationLabel!) 67 | 68 | 69 | envImageView = UIImageView(frame: CGRect(x: 10, y: locationLabel!.bottom + 20, 70 | width:15, height: 20)) 71 | backScrollView?.addSubview(envImageView!) 72 | envImageView?.image = UIImage(named: "annotation_details_env") 73 | 74 | 75 | environmentLabel = UILabel(frame: CGRect(x: 35, y: envImageView!.top, 76 | width: view.width - 20, height: 20)) 77 | environmentLabel?.textColor = .white 78 | environmentLabel?.font = UIFont.boldSystemFont(ofSize: 15) 79 | backScrollView?.addSubview(environmentLabel!) 80 | 81 | 82 | heaImageView = UIImageView(frame: CGRect(x: 10, y: envImageView!.bottom + 10, 83 | width: 17.5, height: 20)) 84 | backScrollView?.addSubview(heaImageView!) 85 | heaImageView?.image = UIImage(named: "annotation_details_altitude") 86 | 87 | 88 | healthLabel = UILabel(frame: CGRect(x: 35, y: heaImageView!.top, 89 | width: view.width - 20, height: 20)) 90 | healthLabel?.textColor = .white 91 | healthLabel?.font = UIFont.boldSystemFont(ofSize: 15) 92 | backScrollView?.addSubview(healthLabel!) 93 | 94 | 95 | contentTextView = UITextView(frame: CGRect(x: 10, y: heaImageView!.bottom + 30, 96 | width: view.width - 20, height: 200)) 97 | backScrollView?.addSubview(contentTextView!) 98 | contentTextView?.delegate = self 99 | contentTextView?.backgroundColor = PJRGB(r: 50, g: 50, b: 50) 100 | contentTextView?.tintColor = .white 101 | contentTextView?.layer.cornerRadius = 8 102 | contentTextView?.font = UIFont.systemFont(ofSize: 20, weight: .light) 103 | contentTextView?.textColor = .white 104 | contentTextView?.layer.masksToBounds = true 105 | 106 | 107 | contentTextViewLeftPadding = contentTextView!.textContainer.lineFragmentPadding 108 | 109 | 110 | contentTextViewTipsLabel = UILabel(frame: CGRect(x: 5, y: 10, 111 | width: contentTextView!.width, height: 20)) 112 | contentTextView?.addSubview(contentTextViewTipsLabel!) 113 | contentTextViewTipsLabel?.font = contentTextView!.font 114 | contentTextViewTipsLabel?.textColor = contentTextView!.textColor 115 | 116 | 117 | photoContentView = UIView(frame: CGRect(x: contentTextView!.left, 118 | y: contentTextView!.bottom + 20, 119 | width: contentTextView!.width, 120 | height: 100)) 121 | photoContentView?.backgroundColor = contentTextView!.backgroundColor 122 | backScrollView?.addSubview(photoContentView!) 123 | photoContentView?.layer.cornerRadius = 8 124 | photoContentView?.layer.masksToBounds = true 125 | 126 | 127 | addPhotoButton = UIButton(frame: CGRect(x: 0, y: 0, 128 | width: 200, height: 80)) 129 | photoContentView?.addSubview(addPhotoButton!) 130 | addPhotoButton?.addTarget(self, 131 | action: #selector(addPhotoButtonTapped), 132 | for: .touchUpInside) 133 | addPhotoButton?.centerX = view.centerX - 10 134 | addPhotoButton?.top = (photoContentView!.height - 80) / 2 135 | addPhotoButton?.setTitleColor(.white, for: .normal) 136 | addPhotoButton?.titleLabel?.font = UIFont.systemFont(ofSize: 15) 137 | addPhotoButton?.setTitle("点击添加照片", for: .normal) 138 | 139 | 140 | addTimeButton = UIButton(frame: CGRect(x: photoContentView!.right - 30, 141 | y: photoContentView!.bottom - headerView!.height - PJStatusHeight, 142 | width: 30, height: 30)) 143 | backScrollView?.addSubview(addTimeButton!) 144 | addTimeButton?.addTarget(self, 145 | action: #selector(showSelectTimeList), 146 | for: .touchUpInside) 147 | addTimeButton?.setImage(UIImage(named: "annotation_details_addTime"), 148 | for: .normal) 149 | 150 | } 151 | 152 | private func initData() { 153 | title = annotationView?.model?.createdTimeString 154 | locationLabel?.text = annotationView?.model?.formatterAddress 155 | 156 | if annotationView!.model!.createdTimeString.contains("/") { 157 | addTimeButton?.isHidden = true 158 | } else { 159 | addTimeButton?.isHidden = false 160 | } 161 | 162 | if let photoImage = PJCoreDataHelper.shared.annotationImage(model: annotationView!.model!) { 163 | clipNewPhotoImage(photoImage) 164 | newPhotoImage = photoImage 165 | } else { 166 | if isHiddenRightBarButton! { 167 | photoContentView?.removeFromSuperview() 168 | } 169 | } 170 | 171 | // MARK: - coreData 172 | let content = PJCoreDataHelper.shared.annotationContent(model: annotationView!.model!) 173 | if content == "" { 174 | if isHiddenRightBarButton! { 175 | contentTextView?.isHidden = true 176 | } else { 177 | contentTextViewTipsLabel?.isHidden = false 178 | contentTextViewTipsLabel?.text = "快来填写签到内容吧~" 179 | } 180 | 181 | updateBackScrollViewContentSize() 182 | } else { 183 | if isHiddenRightBarButton! { 184 | contentTextView?.isEditable = false 185 | } 186 | contentTextViewTipsLabel?.isHidden = true 187 | contentTextView?.text = content 188 | previousContentText = content 189 | 190 | updateView(showTips: false) 191 | } 192 | 193 | // MARK: update frame 194 | if annotationView?.model?.environmentString == "-" { 195 | environmentLabel?.isHidden = true 196 | healthLabel?.isHidden = true 197 | envImageView?.isHidden = true 198 | heaImageView?.isHidden = true 199 | 200 | contentTextView?.top = locationLabel!.bottom + 10 201 | photoContentView?.top = contentTextView!.bottom + 10 202 | } else { 203 | environmentLabel?.text = annotationView?.model?.environmentString 204 | healthLabel?.text = "海拔:" + annotationView!.model!.altitude + "米 步数:" + annotationView!.model!.stepCount 205 | } 206 | } 207 | 208 | 209 | // MARK: - Action 210 | @objc private func showSelectTimeList() { 211 | datePickerView.isHidden = false 212 | UIView.animate(withDuration: 0.25, animations: { 213 | self.datePickerView.bottom = self.view.height 214 | }) { (finished) in 215 | PJTapic.tap() 216 | } 217 | } 218 | 219 | 220 | @objc private func leftBarButtonTapped() { 221 | if title!.contains("/") { 222 | annotationView?.model?.createdTimeString = title! 223 | NotificationCenter.default.post(name: NSNotification.Name(rawValue: PJNotificationName_updateCallouView), 224 | object: nil, 225 | userInfo: [ 226 | "time": title!, 227 | ]) 228 | } 229 | navigationController?.popViewController(animated: true) 230 | } 231 | 232 | 233 | @objc private func rightButtonTapped() { 234 | var isCanSave = 0 235 | 236 | if contentTextView?.text != "" { 237 | isCanSave += 1 238 | } 239 | 240 | if newPhotoImage != nil { 241 | isCanSave += 1 242 | } 243 | 244 | if isCanSave < 1 { 245 | print("签到内容和照片必须选一个哦~") 246 | PJTapic.error() 247 | return 248 | } 249 | 250 | 251 | var isUpdateTime = true 252 | var isSaved = true 253 | var isPhotoImage = true 254 | 255 | 256 | // TODO: 做下提示 257 | if title!.contains("/") { 258 | let formatString = "longitude=\(annotationView!.model!.longitude) and latitude=\(annotationView!.model!.latitude)" 259 | isUpdateTime = PJCoreDataHelper.shared.updateAnnotation(formatString: formatString, updateTime: title!) 260 | } 261 | 262 | 263 | 264 | isSaved = PJCoreDataHelper.shared.addAnnotationContent(content: contentTextView!.text, model: annotationView!.model!) 265 | 266 | if newPhotoImage != nil { 267 | isPhotoImage = PJCoreDataHelper.shared.addAnnotationPhoto(photoImage: newPhotoImage!, 268 | model: annotationView!.model!) 269 | } 270 | 271 | 272 | if isPhotoImage && isSaved && isUpdateTime { 273 | PJTapic.succee() 274 | leftBarButtonTapped() 275 | } else { 276 | PJTapic.error() 277 | } 278 | } 279 | 280 | 281 | @objc private func addPhotoButtonTapped() { 282 | PJTapic.tap() 283 | 284 | let actionSheet = UIAlertController.init(title: "选择照片来源", message: "从相册选择或者相机拍摄一张照片吧~", preferredStyle: .actionSheet) 285 | 286 | let photoAlbumAction = UIAlertAction(title: "相册", style: .default) { (action) in 287 | self.initPhotoPicker() 288 | } 289 | 290 | let cameraAction = UIAlertAction(title: "相机", style: .default) { (action) in 291 | self.initCameraPicker() 292 | } 293 | 294 | let cancleAction = UIAlertAction(title: "取消", style: .cancel) { (action) in 295 | actionSheet.dismiss(animated: true) 296 | } 297 | 298 | actionSheet.addAction(photoAlbumAction) 299 | actionSheet.addAction(cameraAction) 300 | actionSheet.addAction(cancleAction) 301 | 302 | present(actionSheet, animated: true) 303 | 304 | } 305 | 306 | 307 | private func initPhotoPicker() { 308 | let photoPicker = UIImagePickerController() 309 | photoPicker.delegate = self 310 | photoPicker.sourceType = .photoLibrary 311 | present(photoPicker, animated: true) 312 | } 313 | 314 | 315 | private func initCameraPicker() { 316 | if UIImagePickerController.isSourceTypeAvailable(.camera) { 317 | let cameraPicker = UIImagePickerController() 318 | cameraPicker.delegate = self 319 | cameraPicker.sourceType = .camera 320 | present(cameraPicker, animated: true) 321 | } else { 322 | // TODO: 不支持拍照 323 | } 324 | } 325 | 326 | 327 | @objc func image(image:UIImage, 328 | didFinishSavingWithError error:NSError?, 329 | contextInfo:AnyObject) { 330 | if error != nil { 331 | print("保存失败") 332 | } else { 333 | clipNewPhotoImage(image) 334 | print("保存成功") 335 | } 336 | } 337 | 338 | 339 | private func updateBackScrollViewContentSize() { 340 | if annotationView!.model!.createdTimeString.contains("/") { 341 | backScrollView?.contentSize = CGSize(width: 0, 342 | height: photoContentView!.bottom + 10 + headerView!.height) 343 | } else { 344 | backScrollView?.contentSize = CGSize(width: 0, 345 | height: addTimeButton!.bottom + 10 + headerView!.height) 346 | } 347 | 348 | if backScrollView!.contentSize.height < view.height { 349 | backScrollView?.contentSize = CGSize(width: 0, 350 | height: view.height + 1) 351 | } 352 | } 353 | 354 | 355 | func clipNewPhotoImage(_ photoImage:UIImage) { 356 | addPhotoButton?.isHidden = true 357 | 358 | let photoImageSize = photoImage.size 359 | let radio = photoImageSize.width / photoImageSize.height 360 | let photoImageView = UIImageView(frame: CGRect(x: 5, y: 5, 361 | width: photoContentView!.width - 10, 362 | height: photoContentView!.width / radio)) 363 | photoImageView.image = photoImage 364 | photoContentView?.height = photoImageView.height + 10 365 | photoImageView.layer.cornerRadius = 8 366 | photoImageView.layer.masksToBounds = true 367 | 368 | newPhotoImage = photoImage 369 | photoContentView?.addSubview(photoImageView) 370 | 371 | addTimeButton?.top = photoContentView!.bottom + 10 372 | 373 | updateBackScrollViewContentSize() 374 | } 375 | 376 | 377 | private func updateView(showTips: Bool) { 378 | if showTips { 379 | UIView.animate(withDuration: 0.25, animations: { 380 | self.contentTextView?.height = 200 381 | }) { (finished) in 382 | if finished { 383 | UIView.animate(withDuration: 0.25, animations: { 384 | self.photoContentView?.top = self.contentTextView!.bottom + 20 385 | self.addTimeButton?.top = self.photoContentView!.bottom + 10 386 | }, completion: { (finished) in 387 | if finished { 388 | PJTapic.tap() 389 | 390 | self.updateBackScrollViewContentSize() 391 | } 392 | }) 393 | } 394 | } 395 | } else { 396 | let textSize = contentTextView!.attributedText.boundingRect(with: CGSize(width: contentTextView!.width - 2 * contentTextViewLeftPadding!, height: CGFloat(MAXFLOAT)), options: NSStringDrawingOptions.usesLineFragmentOrigin, context: nil) 397 | 398 | UIView.animate(withDuration: 0.25, animations: { 399 | self.contentTextView?.height = textSize.size.height + self.contentTextViewLeftPadding! * 3.5 400 | }) { (finished) in 401 | if finished { 402 | UIView.animate(withDuration: 0.25, animations: { 403 | self.photoContentView?.top = self.contentTextView!.bottom + 20 404 | self.addTimeButton?.top = self.photoContentView!.bottom + 10 405 | }, completion: { (finished) in 406 | if finished { 407 | PJTapic.tap() 408 | 409 | self.updateBackScrollViewContentSize() 410 | } 411 | }) 412 | } 413 | } 414 | } 415 | } 416 | 417 | 418 | func changeDatePickerViewStuts() { 419 | if datePickerView.bottom == view.height { 420 | UIView.animate(withDuration: 0.25, 421 | delay: 0, 422 | options: .curveEaseOut, 423 | animations: { 424 | self.datePickerView.top = self.view.height 425 | }, completion: nil) 426 | } else { 427 | UIView.animate(withDuration: 0.25, 428 | delay: 0, 429 | options: .curveEaseIn, 430 | animations: { 431 | self.datePickerView.bottom = self.view.height 432 | }, completion: nil) 433 | } 434 | } 435 | 436 | 437 | // MARK: - Delegate 438 | func textViewDidEndEditing(_ textView: UITextView) { 439 | guard textView.text != "" else { 440 | return 441 | } 442 | updateView(showTips: false) 443 | } 444 | 445 | 446 | func textViewDidChange(_ textView: UITextView) { 447 | guard textView.text != "" else { 448 | contentTextViewTipsLabel?.isHidden = false 449 | updateView(showTips: true) 450 | return 451 | } 452 | contentTextViewTipsLabel?.isHidden = true 453 | } 454 | 455 | 456 | func imagePickerController(_ picker: UIImagePickerController, 457 | didFinishPickingMediaWithInfo info: [String : Any]) { 458 | let image: UIImage = info[UIImagePickerControllerOriginalImage] as! UIImage 459 | if picker.sourceType == .camera { 460 | UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(image:didFinishSavingWithError:contextInfo:)), nil) 461 | } 462 | 463 | if picker.sourceType == .photoLibrary { 464 | clipNewPhotoImage(image) 465 | } 466 | 467 | dismiss(animated: true, completion: nil) 468 | } 469 | 470 | 471 | func PJDatePickerViewOkButtonTapped(_ dateString: String) { 472 | changeDatePickerViewStuts() 473 | title = dateString 474 | } 475 | 476 | 477 | func PJDatePickerViewCloseButtonTapped() { 478 | changeDatePickerViewStuts() 479 | } 480 | 481 | 482 | // MARK: - lazy load 483 | lazy var datePickerView: PJDatePickerView = { 484 | let datePickerView = PJDatePickerView.newInstance() 485 | datePickerView?.height = 250 486 | datePickerView?.isHidden = true 487 | datePickerView?.top = view.height 488 | datePickerView?.viewDelegate = self 489 | PJInsertRoundingCorners(datePickerView!) 490 | 491 | view.addSubview(datePickerView!) 492 | return datePickerView! 493 | }() 494 | } 495 | -------------------------------------------------------------------------------- /PFollow/Page/InfoFlow/controller/Details/View/PJDatePickerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJDatePickerView.swift 3 | // PFollow 4 | // 5 | // Created by PJHubs on 2018/9/4. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | protocol PJDatePickerViewDelegate { 13 | func PJDatePickerViewCloseButtonTapped() 14 | func PJDatePickerViewOkButtonTapped(_ dateString: String) 15 | } 16 | extension PJDatePickerViewDelegate { 17 | func PJDatePickerViewCloseButtonTapped() {} 18 | func PJDatePickerViewOkButtonTapped(_ dateString: String) {} 19 | } 20 | 21 | 22 | class PJDatePickerView: UIView { 23 | 24 | @IBOutlet weak private var datePickerView: UIDatePicker! 25 | @IBOutlet weak private var closeButton: UIButton! 26 | @IBOutlet weak private var okButton: UIButton! 27 | 28 | var viewDelegate: PJDatePickerViewDelegate? 29 | 30 | 31 | // MARK: Instance 32 | class func newInstance() -> PJDatePickerView? { 33 | let nib = Bundle.main.loadNibNamed("PJDatePickerView", owner: nil, options: nil) 34 | if let view = nib?.first as? PJDatePickerView { 35 | return view 36 | } 37 | return nil 38 | } 39 | 40 | 41 | override func awakeFromNib() { 42 | // datePickerView.maximumDate = Date() 43 | // datePickerView.minimumDate = DateFormatter().date(from: "1920-01-01") 44 | super.awakeFromNib() 45 | } 46 | 47 | 48 | @IBAction func closeButtonTapped(_ sender: UIButton) { 49 | viewDelegate?.PJDatePickerViewCloseButtonTapped() 50 | } 51 | 52 | 53 | @IBAction func okButtonTapped(_ sender: UIButton) { 54 | let timeFormatter = DateFormatter() 55 | timeFormatter.dateFormat = "yyyy/MM/dd" 56 | let dateString = timeFormatter.string(from: datePickerView.date) + " 来过" 57 | viewDelegate?.PJDatePickerViewOkButtonTapped(dateString) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /PFollow/Page/InfoFlow/controller/Details/View/PJDatePickerView.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 | 36 | 45 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /PFollow/Page/InfoFlow/controller/Flow/PJInfoFlowViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJInfoFlowViewController.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/18. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PJInfoFlowViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | override func didReceiveMemoryWarning() { 20 | super.didReceiveMemoryWarning() 21 | // Dispose of any resources that can be recreated. 22 | } 23 | 24 | 25 | /* 26 | // MARK: - Navigation 27 | 28 | // In a storyboard-based application, you will often want to do a little preparation before navigation 29 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 30 | // Get the new view controller using segue.destinationViewController. 31 | // Pass the selected object to the new view controller. 32 | } 33 | */ 34 | 35 | } 36 | -------------------------------------------------------------------------------- /PFollow/Page/Places/Controller/PJPlacesViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJPlacesViewController.swift 3 | // PFollow 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | 12 | class PJPlacesViewController: PJBaseViewController { 13 | 14 | 15 | var annotationModels: [AnnotationModel]? 16 | 17 | private var topView: UIImageView? 18 | private var animator: UIDynamicAnimator? 19 | private var titleLabel: UILabel? 20 | private var skView: SKView? 21 | private var currentModel: AnnotationModel? 22 | 23 | 24 | // MARK: - life cycle 25 | override func viewWillAppear(_ animated: Bool) { 26 | super.viewWillAppear(animated) 27 | self.becomeFirstResponder() 28 | } 29 | 30 | 31 | override func viewWillDisappear(_ animated: Bool) { 32 | super.viewWillDisappear(animated) 33 | self.resignFirstResponder() 34 | } 35 | 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | 40 | initView() 41 | initScene() 42 | } 43 | 44 | override func didReceiveMemoryWarning() { 45 | super.didReceiveMemoryWarning() 46 | } 47 | 48 | 49 | private func initScene() { 50 | skView = SKView(frame: CGRect(x: 0, y: topView!.bottom, 51 | width: view.width, height: view.height - topView!.bottom)) 52 | view.addSubview(skView!) 53 | 54 | let scene = PJPlacesAnnotationScene(size: skView!.frame.size) 55 | scene.scaleMode = .aspectFill 56 | scene.annotationModels = annotationModels 57 | skView?.presentScene(scene) 58 | 59 | skView?.ignoresSiblingOrder = true 60 | // scnView.showsPhysics = true 61 | // skView?.showsFPS = true 62 | // skView?.showsNodeCount = true 63 | 64 | 65 | let skViewButton = UIButton(frame: CGRect(x: skView!.x, y: skView!.y, 66 | width: skView!.width, 67 | height: skView!.height)) 68 | skViewButton.addTarget(self, 69 | action: #selector(bottleViewTapped), 70 | for: .touchUpInside) 71 | view.addSubview(skViewButton) 72 | } 73 | 74 | 75 | private func initView() { 76 | view.backgroundColor = PJRGB(r: 11, g: 11, b: 11) 77 | headerView?.backgroundColor = .white 78 | UIApplication.shared.statusBarStyle = .default 79 | 80 | topView = UIImageView(frame: CGRect(x: -PJSCREEN_WIDTH * 0.1, y: -80 + PJStatusHeight, 81 | width: PJSCREEN_WIDTH * 1.2, height: 160)) 82 | let tap = UITapGestureRecognizer(target: self, 83 | action: #selector(backViewTapGestureClick)) 84 | topView?.addGestureRecognizer(tap) 85 | tap.numberOfTouchesRequired = 1 86 | topView?.isUserInteractionEnabled = true 87 | view.addSubview(topView!) 88 | topView?.image = UIImage(named: "home_cloud") 89 | 90 | 91 | titleLabel = UILabel(frame: CGRect(x: 0, y: -PJStatusHeight, 92 | width: view.width, height: 100)) 93 | if iPhoneX { 94 | titleLabel?.y = 10 95 | } 96 | view.addSubview(titleLabel!) 97 | titleLabel?.numberOfLines = 2 98 | titleLabel?.textAlignment = .center 99 | titleLabel?.text = "" 100 | titleLabel?.font = UIFont.systemFont(ofSize: 22, 101 | weight: .light) 102 | } 103 | 104 | 105 | 106 | // MARK: - Action 107 | @objc private func backViewTapGestureClick() { 108 | self.dismiss(animated: true, completion: nil) 109 | } 110 | 111 | 112 | @objc private func bottleViewTapped() { 113 | if titleLabel?.text != "" { 114 | let annotationView = PJHomeMapAnnotationView(annotation: nil, 115 | reuseIdentifier: nil) 116 | annotationView?.model = currentModel 117 | let vc = PJAnnotationDetailsViewController() 118 | vc.annotationView = annotationView 119 | vc.isHiddenRightBarButton = true 120 | navigationController?.pushViewController(vc, animated: true) 121 | } 122 | } 123 | 124 | 125 | // MARK: - delegate 126 | override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { 127 | if event?.subtype == UIEventSubtype.motionShake { 128 | guard annotationModels?.count != 0 else { 129 | return 130 | } 131 | currentModel = annotationModels![Int(arc4random_uniform(UInt32(annotationModels!.count)))] 132 | titleLabel?.text = currentModel?.formatterAddress 133 | PJTapic.succee() 134 | } 135 | } 136 | 137 | 138 | override var prefersStatusBarHidden: Bool { 139 | return true 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /PFollow/Page/Places/View/Scene/PJPlacesAnnotationScene.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PJAnnotationScene.swift 3 | // PFollow 4 | // 5 | // Created by PJHubs on 2018/9/5. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SpriteKit 11 | import CoreMotion 12 | 13 | import Schedule 14 | 15 | 16 | class PJPlacesAnnotationScene: SKScene { 17 | 18 | var annotationModels: [AnnotationModel]? { 19 | willSet(models) { 20 | willSetAnnotationModels(models!) 21 | } 22 | } 23 | 24 | private let motionManager = CMMotionManager() // 加速度计管理器 25 | private var bottleViwSprite: SKSpriteNode? 26 | 27 | 28 | 29 | // MARK: - life cycle 30 | override init(size: CGSize) { 31 | super.init(size: size) 32 | 33 | initView() 34 | } 35 | 36 | 37 | required init?(coder aDecoder: NSCoder) { 38 | super.init(coder: aDecoder) 39 | } 40 | 41 | 42 | private func initView() { 43 | self.backgroundColor = PJRGB(r: 11, g: 11, b: 11) 44 | 45 | 46 | bottleViwSprite = SKSpriteNode(imageNamed: "places_bgView") 47 | bottleViwSprite?.size = CGSize(width: self.size.width * 0.9, height: self.size.width / 0.656) 48 | bottleViwSprite?.position = CGPoint(x: self.size.width * 0.11, 49 | y: (size.height - bottleViwSprite!.size.height) / 2) 50 | bottleViwSprite?.anchorPoint = CGPoint(x: 0, y: 0) 51 | bottleViwSprite?.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: bottleViwSprite!.frame.origin.x * 0.45, y: bottleViwSprite!.size.height * 0.1, width: bottleViwSprite!.size.width * 0.75, height: bottleViwSprite!.size.height - bottleViwSprite!.size.height * 0.3)) 52 | bottleViwSprite?.physicsBody?.density = 1000 53 | addChild(bottleViwSprite!) 54 | 55 | 56 | let leftBox = SKSpriteNode(imageNamed: "places_null") 57 | leftBox.size = CGSize(width: 50, height: 50) 58 | leftBox.position = CGPoint(x: bottleViwSprite!.frame.origin.x * 0.3, y: bottleViwSprite!.size.height * 0.8) 59 | leftBox.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50)) 60 | leftBox.physicsBody?.isDynamic = false 61 | bottleViwSprite?.addChild(leftBox) 62 | 63 | 64 | let rightBox = SKSpriteNode(imageNamed: "places_null") 65 | rightBox.size = CGSize(width: 50, height: 50) 66 | let rightBoxRight = leftBox.frame.origin.x + leftBox.size.width 67 | rightBox.position = CGPoint(x: rightBoxRight + bottleViwSprite!.size.width * 0.7, 68 | y: bottleViwSprite!.size.height * 0.8) 69 | rightBox.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50)) 70 | rightBox.physicsBody?.isDynamic = false 71 | bottleViwSprite?.addChild(rightBox) 72 | 73 | 74 | startMonitoringAcceleration() 75 | } 76 | 77 | 78 | // MARK: - 开启加速度计 79 | func startMonitoringAcceleration(){ 80 | if motionManager.isAccelerometerAvailable { 81 | // 感应时间 82 | motionManager.accelerometerUpdateInterval = 0 83 | motionManager.startAccelerometerUpdates(to: OperationQueue.current!) { (data, error) in 84 | guard let accelerometerData = data else { 85 | return 86 | } 87 | let acceleration = accelerometerData.acceleration 88 | self.physicsWorld.gravity = CGVector(dx: acceleration.x * 9.8, 89 | dy: acceleration.y * 9.8) 90 | } 91 | } 92 | } 93 | 94 | 95 | private func willSetAnnotationModels(_ models: [AnnotationModel]) { 96 | for i in 0 ..< models.count { 97 | Schedule.after(Double(0.05 * Double(i)).second).do { 98 | DispatchQueue.main.async { 99 | let annotationSprite = SKSpriteNode(imageNamed: "home_map_makers_02") 100 | let randomX = self.bottleViwSprite!.size.width - self.bottleViwSprite!.frame.origin.x * 2 - 40 101 | let annotationX = CGFloat(arc4random_uniform(UInt32(randomX))) + self.bottleViwSprite!.frame.origin.x 102 | annotationSprite.position = CGPoint(x: annotationX, y: self.bottleViwSprite!.size.height * 0.8) 103 | annotationSprite.size = CGSize(width: 18, height: 18) 104 | annotationSprite.physicsBody = SKPhysicsBody(circleOfRadius: annotationSprite.size.height / 3) 105 | // 置于最底层 106 | annotationSprite.zPosition = -1 107 | 108 | self.bottleViwSprite?.addChild(annotationSprite) 109 | } 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /PFollow/mapView.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/PFollow/mapView.data -------------------------------------------------------------------------------- /PFollowTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PFollowTests/PFollowTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PFollowTests.swift 3 | // PFollowTests 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PFollow 11 | 12 | class PFollowTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /PFollowUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PFollowUITests/PFollowUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PFollowUITests.swift 3 | // PFollowUITests 4 | // 5 | // Created by pjpjpj on 2018/8/12. 6 | // Copyright © 2018年 pjpjpj. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class PFollowUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AMap3DMap (6.4.0): 3 | - AMapFoundation (~> 1.5) 4 | - AMapFoundation (1.5.4) 5 | - AMapLocation (2.6.1): 6 | - AMapFoundation (~> 1.4) 7 | - AMapSearch (6.1.1): 8 | - AMapFoundation (~> 1.4) 9 | - IQKeyboardManagerSwift (6.1.1) 10 | - Schedule (0.1.0) 11 | 12 | DEPENDENCIES: 13 | - AMap3DMap 14 | - AMapLocation 15 | - AMapSearch 16 | - IQKeyboardManagerSwift 17 | - Schedule 18 | 19 | SPEC REPOS: 20 | https://github.com/cocoapods/specs.git: 21 | - AMap3DMap 22 | - AMapFoundation 23 | - AMapLocation 24 | - AMapSearch 25 | - IQKeyboardManagerSwift 26 | - Schedule 27 | 28 | SPEC CHECKSUMS: 29 | AMap3DMap: ebfb4e202c2bee9c3a6c296264e29f12436b6379 30 | AMapFoundation: bff8688722e886feda4edd77f1443746ddd7e2e2 31 | AMapLocation: cf024b53dc03663efb4ea4bcce27d4cce58bb97a 32 | AMapSearch: 3dcbf85c403a2a0fe0eda5318caabc272b31f5e1 33 | IQKeyboardManagerSwift: 977affaeb4d6e971975c7790a6850f31d38f1207 34 | Schedule: 62cd2defb026a3607349b2a2b1bd8ec3fd12cccd 35 | 36 | PODFILE CHECKSUM: 9d2ccef08c058577579e43cdefb2bb103f76061f 37 | 38 | COCOAPODS: 1.5.3 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![IMG_0305.jpg](https://i.loli.net/2018/11/03/5bddc2dc527a3.jpg) 4 | 5 | ## 简介 6 | PFollow 为今年暑假新开的项目,期末考试完后和女票一同去了重庆青海西安十日游。在游玩的过程中,使用众多 app 协助旅行,在青海时,会经常到查看 iOS 自带的“指南针” app 看当前海拔。同时也因为在高原的天气变化多端,会非常注意第二天的天气等等这些习惯就被强制性的养成了。 7 | 8 | 旅行结束后,躺在宿舍床上慢慢回想这旅行的这段时间,很多回忆都留在了一张张照片上,虽然照片会带上当时拍摄时的一些地点及其它信息,相册也会自动识别并对照片进行归类,甚至还能自动归档生成一段回忆视频。 9 | 10 | 但是我总觉得还是不能很好的回忆起当时旅行中的一些美好的细节,因为往往会因为时间或者地点的因素被覆盖掉,却会在故地重游时再现。当时我脑海里就在想,如果我能够把游客此时所在地点配上文字和图片,形成一个个的“点”打在地图上,日后回想起这段旅程时,就可以顺着地图上的“点”,回忆起当初在这里发生的一些事情,这该是一件多么有趣的事情啊! 11 | 12 | 遂开干! 13 | 14 | ## 调研 15 | 我明确知道这种需求是一定被玩烂了的,就开始去 app store 中搜了一遍旅行类、记录类的 app,甚至也把知乎上关于“旅行记录/打卡”的相关问题都过了一遍,发现确实已经有产品在解决这些问题,总结出了以下几点: 16 | 17 | * 体验不佳; 18 | * 太丑; 19 | * 广告太多; 20 | * 忘了初心(名义上是旅行打卡,实质则为其它,例如:推销旅游产品) 21 | * 过于复杂; 22 | * 旅行打卡不是主要业务,入口太深 23 | 24 | 以上几点当然是都经过考虑的,有可能是因为单纯做“旅行打卡”工具类 app 难以存活;玩不出新花样;用户变现等等问题导致产品的方向发生了变化,都能理解。 25 | 26 | 但是,既然各个开发商都不愿意做这赔本事情,那为了满足个人需要就自己开干。 27 | 28 | ## 设计 29 | 在设计阶段花的时间不多,大概只用了前前后后合计不到四五天的时间就搞定了 1.0 的设计,因为其实自己在大一的时候就萌生出了做类似的旅游产品,很多地方可以直接用之前的设计和逻辑。 30 | 31 | 基本上把 app store 中“旅游”、“旅游打卡”、“旅行记录”等关键词下的 app 都使用并思考过了至少一遍,也结合了自己的使用习惯,使用 Sketch 设计出了一套符合个人习惯的 UI 。 32 | 33 | 但是非常遗憾,辛辛苦苦在咖啡厅调了一天,然后重装了一次系统偏偏就是忘了备份它!!!导致代码写到后边出的成果有些地方比较“怪异”。 34 | 35 | ## 实施 36 | 梳理了一遍 PFollow 中需要用到的知识,如下所示: 37 | 38 | * 自定义地图(高德、百度、MapKit) 39 | * GPS 40 | * 陀螺仪 41 | * 海拔 42 | * UI Dynamic 43 | * SpriteKit 44 | * CloudKit 45 | * CoreData 46 | * 数据库设计 47 | * UIKit 48 | * Tapic Engine 49 | * 读取相册 50 | * 相机 51 | * 其它 52 | 53 | 在这个项目中我对“自定义地图”、“UI Dynamic”、“Core Data”没有十足的把握,衡量了一下,可以不用停下目前正在做的事情把精力全部投入进行学习,因为我有个习惯,如果发现准备要做的事情超出了自己目前的知识储备,就会 all in 到学习这个事情中去,弄懂后再开始。 54 | 55 | 到最后我发现其实坑还是在 “Core Data” 这块,因为它跟我以往用过的、打着相同口号的 ORM 框架有很多不一样的地方,所以不光是思维的转换上需要斟酌斟酌,还有写法上也不太一样,需要熟悉一段时间。 56 | 57 | 不过好在到现在已经对 “Core Data” 有了一定的了解,并且也把习得的知识用在了实习公司的产品中,替换了大部分数据缓存策略。 58 | 59 | ## 展示 60 | ### 地图 61 | 62 | 63 | ### 缩放地图 64 | 65 | 66 | ### 放大地图 67 | 68 | 69 | ### 聚点详情 70 | 71 | 72 | ### 聚点瓶 73 | 74 | 75 | 76 | ## 上线计划 77 | 目前 PFollow 暂无上线计划,因为我在前段时间的一篇文章中了解到 Instagram 居然就是做“地点签到”起家的,后来因为各种原因而改变了产品方向,在当时我瞬间就明白了,Ins 一定是遇到了某些问题,这些问题会在 PFollow 上线后随着时间的流逝而暴露出来,我坚信! 78 | 79 | 因此,PFollow 我将会以开源项目的方式进行下去。 80 | 81 | ## 思考 82 | 在开发 PFollow 的过程中带给我最多的思考还是如何把自己的想法转换成实际一行行的代码,这其中会有许多的妥协和坚持,我们最终坚持下来的东西很有可能就是我们当时内心深处最真实的想法,妥协掉一些累赘。 83 | 84 | 当然 1.0 版本还有些许缺陷,很大一部分原因是因为开学了我要有其它的事情去做了,PFollow 算是在这个炎热的暑假中让我静心学习的好助手,让我重新调整了自己的状态迎接大学最后一年。 85 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/windstormeye/PFollow/d1f7abb9cba9fd35594fadbf68da5c51d078f603/banner.png -------------------------------------------------------------------------------- /podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '11.0' 2 | target 'PFollow' do 3 | 4 | pod 'AMap3DMap' #3D地图 SDK 5 | pod 'AMapLocation' # 定位SDK 6 | pod 'AMapSearch' # 搜索功能 SDK 7 | 8 | pod 'IQKeyboardManagerSwift' 9 | pod 'Schedule' 10 | 11 | end 12 | --------------------------------------------------------------------------------