├── .gitignore
├── LICENSE
├── Podfile
├── Podfile.lock
├── README.md
├── Tests
├── RunTests.sh
├── Tests-Info.plist
└── main.m
├── V2EX.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── xcshareddata
│ └── xcschemes
│ └── Tests.xcscheme
├── V2EX.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── V2EX.xccheckout
└── V2EX
├── Base.lproj
└── Main.storyboard
├── Controllers
├── UIViewController+MBProgressHUD.h
├── UIViewController+MBProgressHUD.m
├── UIViewController+Size.h
├── UIViewController+Size.m
├── UIViewController+V2EXJump.h
├── UIViewController+V2EXJump.m
├── V2EXAfterTopicActionDelegate.h
├── V2EXLatestTopicsViewController.h
├── V2EXLatestTopicsViewController.m
├── V2EXMenuViewController.h
├── V2EXMenuViewController.m
├── V2EXMyRepliesViewController.h
├── V2EXMyRepliesViewController.m
├── V2EXMyTopicsViewController.h
├── V2EXMyTopicsViewController.m
├── V2EXNewTopicViewController.h
├── V2EXNewTopicViewController.m
├── V2EXNodesListViewController.h
├── V2EXNodesListViewController.m
├── V2EXNotificationCenterViewController.h
├── V2EXNotificationCenterViewController.m
├── V2EXReplyTopicViewController.h
├── V2EXReplyTopicViewController.m
├── V2EXRootViewController.h
├── V2EXRootViewController.m
├── V2EXSelfViewController.h
├── V2EXSelfViewController.m
├── V2EXSettingsViewController.h
├── V2EXSettingsViewController.m
├── V2EXSingleTopicViewController.h
├── V2EXSingleTopicViewController.m
├── V2EXTableViewController.h
├── V2EXTableViewController.m
├── V2EXTopicsListInSingleNodeViewController.h
├── V2EXTopicsListInSingleNodeViewController.m
├── V2EXUserLoginViewController.h
└── V2EXUserLoginViewController.m
├── Images.xcassets
├── AppIcon.appiconset
│ ├── Contents.json
│ ├── Icon-40.png
│ ├── Icon-40@2x-1.png
│ ├── Icon-40@2x.png
│ ├── Icon-60@2x.png
│ ├── Icon-72.png
│ ├── Icon-72@2x.png
│ ├── Icon-76.png
│ ├── Icon-76@2x.png
│ ├── Icon-Small-1.png
│ ├── Icon-Small-50.png
│ ├── Icon-Small-50@2x.png
│ ├── Icon-Small.png
│ ├── Icon-Small@2x-1.png
│ ├── Icon-Small@2x.png
│ ├── Icon.png
│ └── Icon@2x.png
├── IconBack.imageset
│ ├── Contents.json
│ └── IconBack@2x.png
├── IconChatting.imageset
│ ├── Contents.json
│ └── chatting.png
├── IconEmpty.imageset
│ ├── Contents.json
│ └── IconEmpty@2x.png
├── IconHome.imageset
│ ├── Contents.json
│ └── IconHome@2x.png
├── IconKey.imageset
│ ├── Contents.json
│ └── IconKey@2x.png
├── IconMenu.imageset
│ ├── Contents.json
│ ├── icon_menu.png
│ └── icon_menu@2x.png
├── IconProfile.imageset
│ ├── Contents.json
│ └── IconProfile@2x.png
├── IconRecent.imageset
│ ├── Contents.json
│ ├── IconRecent@2x.png
│ └── timer-vector.psd
├── IconReply.imageset
│ ├── Contents.json
│ └── speech-bubble-vector.png
├── IconSettings.imageset
│ ├── Contents.json
│ └── IconSettings@2x.png
├── LaunchImage.launchimage
│ ├── Contents.json
│ ├── Default@1536x2048-1.png
│ ├── Default@1536x2048.png
│ ├── Default@640x1136-1.png
│ ├── Default@640x960.png
│ ├── Default@768x1024-1.png
│ └── Default@768x1024.png
├── LaunchImage2.launchimage
│ ├── Contents.json
│ ├── Default@1024x748.png
│ ├── Default@1024x768-1.png
│ ├── Default@1024x768.png
│ ├── Default@1536x2008.png
│ ├── Default@1536x2048-1.png
│ ├── Default@1536x2048.png
│ ├── Default@2048x1496.png
│ ├── Default@2048x1536-1.png
│ ├── Default@2048x1536.png
│ ├── Default@320x480.png
│ ├── Default@640x1136-1.png
│ ├── Default@640x1136.png
│ ├── Default@640x960-1.png
│ ├── Default@640x960.png
│ ├── Default@768x1004.png
│ ├── Default@768x1024-1.png
│ └── Default@768x1024.png
├── Stars.imageset
│ ├── Contents.json
│ └── Stars@2x.png
└── avatar_large.imageset
│ ├── Contents.json
│ └── avatar_large.png
├── Models
├── Data
│ ├── NodesList
│ │ ├── NodesAll.json
│ │ └── NodesList.plist
│ └── repliesList.css
├── V2EXAbstractModel.h
├── V2EXAbstractModel.m
├── V2EXNodesListModel.h
├── V2EXNodesListModel.m
├── V2EXNormalModel.h
├── V2EXNormalModel.m
└── V2EXRequestDataDelegate.h
├── ThirdParties
└── DRPaginatedScrollView
│ ├── DRPaginatedScrollView.h
│ └── DRPaginatedScrollView.m
├── Utilities
├── Network
│ ├── JSONAPIURI.h
│ ├── NormalAPIURI.h
│ ├── V2EXApiClient.h
│ ├── V2EXApiClient.m
│ ├── V2EXHttpRequestSerializer.h
│ └── V2EXHttpRequestSerializer.m
├── TFHpple+V2EXMethod.h
├── TFHpple+V2EXMethod.m
├── TFHppleElement+V2EXMethod.h
├── TFHppleElement+V2EXMethod.m
├── UI
│ ├── UIView+FrameMethods.h
│ ├── UIView+FrameMethods.m
│ ├── V2EXAppSize.h
│ ├── V2EXAppSize.m
│ ├── V2EXMBProgressHUDUtil.h
│ └── V2EXMBProgressHUDUtil.m
├── V2EXGlobalCache.h
├── V2EXGlobalCache.m
├── V2EXStringUtil.h
└── V2EXStringUtil.m
├── V2EX-Info.plist
├── V2EX-Prefix.pch
├── V2EXAppDelegate.h
├── V2EXAppDelegate.m
├── Views
├── Categories
│ ├── UIView+FrameMethods.h
│ └── UIView+FrameMethods.m
└── TableViewCells
│ ├── V2EXNodesListCell.h
│ ├── V2EXNodesListCell.m
│ ├── V2EXNodesListCell.xib
│ ├── V2EXTopicsListCell.h
│ ├── V2EXTopicsListCell.m
│ ├── V2EXTopicsListCell.xib
│ ├── V2EXUserTopicsListCell.h
│ ├── V2EXUserTopicsListCell.m
│ └── V2EXUserTopicsListCell.xib
├── en.lproj
└── InfoPlist.strings
└── main.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS X
2 | .DS_Store
3 |
4 | # Xcode
5 | build/
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | xcuserdata
15 | *.xccheckout
16 | profile
17 | *.moved-aside
18 | DerivedData
19 | *.hmap
20 | *.ipa
21 |
22 | # CocoaPods
23 | Pods
24 |
25 | #GHUnit
26 | Tests
27 | Tests/MyTest.m
28 | Tests/MyTest.m
29 |
30 | Tests/MyTest.m
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) < 2014 > < WildCat >
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '7.0'
2 | pod 'AFNetworking', '~> 2.1.0'
3 | pod 'RESideMenu', '~> 3.4'
4 | pod 'Hpple', :git => 'https://github.com/topfunky/hpple.git'
5 | pod 'MBProgressHUD'
6 | pod 'SVPullToRefresh'
7 | pod 'SDWebImage'
8 | pod 'HMSegmentedControl', '~> 1.3.0'
9 | pod 'Masonry'
10 | pod 'FMDB'
11 | pod 'DDPageControl'
12 | pod 'DTCoreText'
13 | #pod 'MultiLayerNavigation', :git => 'https://github.com/myoula/MultiLayerNavigation.git'
14 |
15 | #JLRubyChina
16 | #pod 'Nimbus', '~> 1.0.0'
17 | #pod 'MTStatusBarOverlay'
18 | #pod 'PPRevealSideViewController'
19 | #pod 'SSKeychain'
20 | #pod 'HPGrowingTextView'
21 | #pod 'SDSegmentedControl'
22 | #pod 'NSStringEmojize'
23 | #pod 'LTUpdate', '~> 0.0.2'
24 | #pod 'RTagCloudView'
25 | #pod 'NSAttributedStringMarkdownParser', '~> 0.0.1'
26 |
27 | target :Tests do
28 | pod 'GHUnit'
29 | end
30 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AFNetworking (2.1.0):
3 | - AFNetworking/NSURLConnection
4 | - AFNetworking/NSURLSession
5 | - AFNetworking/Reachability
6 | - AFNetworking/Security
7 | - AFNetworking/Serialization
8 | - AFNetworking/UIKit
9 | - AFNetworking/NSURLConnection (2.1.0):
10 | - AFNetworking/Reachability
11 | - AFNetworking/Security
12 | - AFNetworking/Serialization
13 | - AFNetworking/NSURLSession (2.1.0):
14 | - AFNetworking/NSURLConnection
15 | - AFNetworking/Reachability (2.1.0)
16 | - AFNetworking/Security (2.1.0)
17 | - AFNetworking/Serialization (2.1.0)
18 | - AFNetworking/UIKit (2.1.0):
19 | - AFNetworking/NSURLConnection
20 | - DDPageControl (0.1)
21 | - DTCoreText (1.6.11):
22 | - DTFoundation/Core (~> 1.6.1)
23 | - DTFoundation/DTHTMLParser (~> 1.6.1)
24 | - DTFoundation/UIKit (~> 1.6.1)
25 | - DTFoundation/Core (1.6.2)
26 | - DTFoundation/DTHTMLParser (1.6.2):
27 | - DTFoundation/Core
28 | - DTFoundation/UIKit (1.6.2):
29 | - DTFoundation/Core
30 | - FMDB (2.2):
31 | - FMDB/standard
32 | - FMDB/common (2.2)
33 | - FMDB/standard (2.2):
34 | - FMDB/common
35 | - GHUnit (0.5.9)
36 | - HMSegmentedControl (1.3.0)
37 | - Hpple (0.2.0)
38 | - Masonry (0.4.0)
39 | - MBProgressHUD (0.8)
40 | - RESideMenu (3.4)
41 | - SDWebImage (3.6):
42 | - SDWebImage/Core
43 | - SDWebImage/Core (3.6)
44 | - SVPullToRefresh (0.4.1)
45 |
46 | DEPENDENCIES:
47 | - AFNetworking (~> 2.1.0)
48 | - DDPageControl
49 | - DTCoreText
50 | - FMDB
51 | - GHUnit
52 | - HMSegmentedControl (~> 1.3.0)
53 | - Hpple (from `https://github.com/topfunky/hpple.git`)
54 | - Masonry
55 | - MBProgressHUD
56 | - RESideMenu (~> 3.4)
57 | - SDWebImage
58 | - SVPullToRefresh
59 |
60 | EXTERNAL SOURCES:
61 | Hpple:
62 | :git: https://github.com/topfunky/hpple.git
63 |
64 | SPEC CHECKSUMS:
65 | AFNetworking: c7d7901a83f631414c7eda1737261f696101a5cd
66 | DDPageControl: ed243202fe4e210f4f2c6e7de26e3c06260c6cd6
67 | DTCoreText: 4d56bb74ff05a3b2415fc15fccc4b5689ed3119c
68 | DTFoundation: 7c69990fa3cb7655cafa95ab59c3fcea014460f0
69 | FMDB: e0dd09464b4e1a1cd7fa51ea835ae821b5656865
70 | GHUnit: 2f3ea0192633e92afaef959476a8d28db179797d
71 | HMSegmentedControl: 3a58d4fc011e40c93c5299b9e8283e89c32f4c64
72 | Hpple: 75ce7943178ebff8812698cb11ade69dea9db141
73 | Masonry: d673617c9cbc24aaea90fba20b10e47fd9970a39
74 | MBProgressHUD: 2bbc6f470111daf7f3eaa4eb12b8cbf01c4c0622
75 | RESideMenu: d8d6330052e0348718826045b912a1c71748357d
76 | SDWebImage: 7eb273e54ca8a2e506ad9bd5e80c35b2a4d7c95e
77 | SVPullToRefresh: 61a0e4bd12bd6f8e3465909810b0fbeb1a28d5f2
78 |
79 | COCOAPODS: 0.32.1
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | V2EX-iOS
2 | ========
3 | The iOS client for V2EX.
4 |
5 | How to start
6 | ========
7 | This project is using CocoaPods. To get more information about it, please go to the following link:
8 | http://code4app.com/article/cocoapods-install-usage
9 |
10 | **And if you are living inside the Great Fire Wall, you might have to use a mirror of CocoaPods Specs, see about this:**
11 | http://www.v2ex.com/t/110435
12 |
13 | TODO
14 | ========
15 | 1. Display images in topics(https://github.com/imWildCat/V2EX-iOS/issues/5)
16 | 2. Fix request time out error in Latest Topics Controller (https://github.com/imWildCat/V2EX-iOS/issues/3)
17 | 3. Add support for "My favourite Nodes" & "My favourite Topics"
18 | 4. Fix line break bug in NewTopicController & ReplyTopicController(https://github.com/imWildCat/V2EX-iOS/issues/4)
19 | 5. Remove singleton of V2EXApiClient
20 |
21 | Author's message
22 | ========
23 | I'm a beginner of programming. As a result, there are many shortcomings of this project. If you find them, please tell me.
24 | In addition, I'm also a English learners who attempt to write code comments in English. There might be a lot of communication confusions. Please spur on me.
25 |
26 | By the way, if you'd like to use Chinese. Just do it. And you can ask me to translate some of my poor English into Chinese XD.
27 | 顺便说下,如果您希望使用汉语,那就用吧。您也可以要求我把一些说得不好的英文翻译为中文。
28 |
29 | Thank you very much!
30 |
31 | LICENSE
32 | ========
33 | The MIT License.
34 |
--------------------------------------------------------------------------------
/Tests/RunTests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # If we aren't running from the command line, then exit
4 | if [ "$GHUNIT_CLI" = "" ] && [ "$GHUNIT_AUTORUN" = "" ]; then
5 | exit 0
6 | fi
7 |
8 | TEST_TARGET_EXECUTABLE_PATH="$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH"
9 |
10 | if [ ! -e "$TEST_TARGET_EXECUTABLE_PATH" ]; then
11 | echo ""
12 | echo " ------------------------------------------------------------------------"
13 | echo " Missing executable path: "
14 | echo " $TEST_TARGET_EXECUTABLE_PATH."
15 | echo " The product may have failed to build."
16 | echo " ------------------------------------------------------------------------"
17 | echo ""
18 | exit 1
19 | fi
20 |
21 | RUN_CMD="ios-sim launch \"$TEST_TARGET_EXECUTABLE_PATH\""
22 |
23 | echo "Running: $RUN_CMD"
24 | set +o errexit # Disable exiting on error so script continues if tests fail
25 | eval $RUN_CMD
26 | RETVAL=$?
27 | set -o errexit
28 |
29 | if [ -n "$WRITE_JUNIT_XML" ]; then
30 | MY_TMPDIR=`/usr/bin/getconf DARWIN_USER_TEMP_DIR`
31 | RESULTS_DIR="${MY_TMPDIR}test-results"
32 |
33 | if [ -d "$RESULTS_DIR" ]; then
34 | `$CP -r "$RESULTS_DIR" "$BUILD_DIR" && rm -r "$RESULTS_DIR"`
35 | fi
36 | fi
37 |
38 | exit $RETVAL
39 |
--------------------------------------------------------------------------------
/Tests/Tests-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDisplayName
6 | ${PRODUCT_NAME}
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | tests.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1.0
21 | LSRequiresIPhoneOS
22 |
23 | UISupportedInterfaceOrientations
24 |
25 | UIInterfaceOrientationPortrait
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Tests/main.m:
--------------------------------------------------------------------------------
1 | // This file auto-generated by ghunit gem
2 | #import
3 |
4 | int main(int argc, char *argv[]) {
5 | @autoreleasepool {
6 | return UIApplicationMain(argc, argv, nil, @"GHUnitIOSAppDelegate");
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/V2EX.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/V2EX.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
53 |
54 |
60 |
61 |
62 |
63 |
65 |
66 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/V2EX.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/V2EX.xcworkspace/xcshareddata/V2EX.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | D76D0B0A-B2E3-4A20-9499-1EA60F5DCFD9
9 | IDESourceControlProjectName
10 | V2EX
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | 31FB247D-598F-41B1-A4F5-E75D1F035604
14 | file:///Users/WildCat/Library/Caches/CocoaPods/GitHub/b950e0b31a1d99b68df85d2c63a4ad3e774cc0c5/
15 | CF0B353A-D8A3-4BCD-B987-321443E300F4
16 | https://github.com/imWildCat/V2EX-iOS.git
17 |
18 | IDESourceControlProjectPath
19 | V2EX.xcworkspace
20 | IDESourceControlProjectRelativeInstallPathDictionary
21 |
22 | 31FB247D-598F-41B1-A4F5-E75D1F035604
23 | ../Pods/hpple
24 | CF0B353A-D8A3-4BCD-B987-321443E300F4
25 | ..
26 |
27 | IDESourceControlProjectURL
28 | https://github.com/imWildCat/V2EX-iOS.git
29 | IDESourceControlProjectVersion
30 | 110
31 | IDESourceControlProjectWCCIdentifier
32 | CF0B353A-D8A3-4BCD-B987-321443E300F4
33 | IDESourceControlProjectWCConfigurations
34 |
35 |
36 | IDESourceControlRepositoryExtensionIdentifierKey
37 | public.vcs.git
38 | IDESourceControlWCCIdentifierKey
39 | 31FB247D-598F-41B1-A4F5-E75D1F035604
40 | IDESourceControlWCCName
41 | hpple
42 |
43 |
44 | IDESourceControlRepositoryExtensionIdentifierKey
45 | public.vcs.git
46 | IDESourceControlWCCIdentifierKey
47 | CF0B353A-D8A3-4BCD-B987-321443E300F4
48 | IDESourceControlWCCName
49 | V2EX-iOS
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+MBProgressHUD.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+MBProgressHUD.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/5/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIViewController (MBProgressHUD)
12 |
13 | - (void) showProgressView;
14 | - (void) hideProgressView;
15 | - (void) showMessage:(NSString *)message;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+MBProgressHUD.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+MBProgressHUD.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/5/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "UIViewController+MBProgressHUD.h"
10 | #import "V2EXMBProgressHUDUtil.h"
11 |
12 | @implementation UIViewController (MBProgressHUD)
13 |
14 | -(void)showProgressView {
15 | [V2EXMBProgressHUDUtil showGlobalProgressHUDWithTitle:nil];
16 | }
17 |
18 | -(void)hideProgressView {
19 | [V2EXMBProgressHUDUtil dismissGlobalHUD];
20 | }
21 |
22 | -(void)showMessage:(NSString *)message {
23 | [V2EXMBProgressHUDUtil showMessage:message];
24 | }
25 | @end
26 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+Size.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Size.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/9/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIViewController (Size)
12 |
13 | - (CGFloat)getHeight;
14 | - (CGFloat)getWidth;
15 | - (CGFloat)getStatusBarHeight;
16 | - (CGFloat)getNavBarHeight;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+Size.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+Size.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/9/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "UIViewController+Size.h"
10 | #import "V2EXAppSize.h"
11 |
12 | @implementation UIViewController (Size)
13 |
14 | - (CGFloat)getHeight {
15 | return [V2EXAppSize getHeightWithController:self];
16 | }
17 |
18 | - (CGFloat)getWidth {
19 | return [V2EXAppSize getWidthWithController:self];
20 | }
21 |
22 | - (CGFloat)getStatusBarHeight {
23 | return [V2EXAppSize getStatusBarHeight];
24 | }
25 |
26 | - (CGFloat)getNavBarHeight {
27 | return [V2EXAppSize getNavBarHeight4Controller:self];
28 | }
29 |
30 | @end
31 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+V2EXJump.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+V2EXJump.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/3/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "V2EXTableViewController.h"
11 |
12 | @interface V2EXTableViewController (V2EXJump)
13 |
14 | - (void)pushToUserLoginController;
15 | - (void)pushToUserLoginController:(BOOL)isLoadFromSeflController;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Controllers/UIViewController+V2EXJump.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+V2EXJump.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/3/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "UIViewController+V2EXJump.h"
10 | #import "V2EXUserLoginViewController.h"
11 |
12 | @implementation UIViewController (V2EXJump)
13 |
14 | - (void)pushToUserLoginController {
15 | [self pushToUserLoginController:NO];
16 | }
17 |
18 | - (void)pushToUserLoginController:(BOOL)isLoadFromSeflController {
19 | V2EXUserLoginViewController *userLoginController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"userLoginController"];
20 | userLoginController.isLoadFromSeflController = isLoadFromSeflController;
21 | [self.navigationController pushViewController:userLoginController animated:YES];
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXAfterTopicActionDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAfterTopicActionDelegate
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/6/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol V2EXAfterTopicActionDelegate
12 |
13 | @optional
14 |
15 | - (void)afterCreateTopic:(NSData *)data;
16 | - (void)afterReplyTopic:(NSData *)data;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXLatestTopicsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXLatestTopicsViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "UIViewController+MBProgressHUD.h"
12 | #import "V2EXTableViewController.h"
13 | #import "V2EXNormalModel.h"
14 |
15 | @interface V2EXLatestTopicsViewController : V2EXTableViewController
16 |
17 | + (V2EXLatestTopicsViewController *)sharedController;
18 | - (IBAction)showMenu:(id)sender;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXLatestTopicsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXLatestTopicsViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXLatestTopicsViewController.h"
10 | #import "V2EXTopicsListCell.h"
11 | #import "V2EXStringUtil.h"
12 |
13 | @interface V2EXLatestTopicsViewController ()
14 |
15 | @end
16 |
17 | @implementation V2EXLatestTopicsViewController
18 |
19 | + (V2EXLatestTopicsViewController *)sharedController
20 | {
21 | static V2EXLatestTopicsViewController *_sharedLatestTopicsViewControllerInstance = nil;
22 | static dispatch_once_t predicate;
23 | dispatch_once(&predicate, ^{
24 | _sharedLatestTopicsViewControllerInstance = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"latestTopicsController"];
25 | });
26 |
27 | return _sharedLatestTopicsViewControllerInstance;
28 | }
29 |
30 | - (void)viewDidLoad
31 | {
32 | [super viewDidLoad];
33 |
34 | // Uncomment the following line to preserve selection between presentations.
35 | // self.clearsSelectionOnViewWillAppear = NO;
36 |
37 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
38 | // self.navigationItem.rightBarButtonItem = self.editButtonItem;
39 |
40 | [self showProgressView];
41 | [self loadData];
42 | }
43 |
44 | - (void)didReceiveMemoryWarning
45 | {
46 | [super didReceiveMemoryWarning];
47 | // Dispose of any resources that can be recreated.
48 | }
49 |
50 | - (void)loadData {
51 | if ([self canStartNewLoading]) {
52 | _loadingStatus = 1;
53 | [self loadListData];
54 | }
55 | }
56 |
57 | - (void)loadListData {
58 | [self.model getLatestTopics];
59 | }
60 |
61 | - (void)loadTopicData:(NSUInteger)ID {
62 | if ([self canStartNewLoading]) {
63 | _loadingStatus = 2;
64 | [self.model getTopicWithID:ID];
65 | [self showProgressView];
66 | }
67 | }
68 |
69 |
70 | #pragma mark - Table view data source
71 |
72 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
73 | {
74 | static NSString *CellIdentifier = @"topicsListCell";
75 |
76 | // static BOOL nibsRegistered = NO;
77 | // if (!nibsRegistered) {
78 | // UINib *nib = [UINib nibWithNibName:@"V2EXTopicsListCell" bundle:nil];
79 | // [tableView registerNib:nib forCellReuseIdentifier:CellIdentifier];
80 | // nibsRegistered = YES;
81 | // }
82 | UINib *nib = [UINib nibWithNibName:@"V2EXTopicsListCell" bundle:nil];
83 | [tableView registerNib:nib forCellReuseIdentifier:CellIdentifier];
84 |
85 | V2EXTopicsListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
86 |
87 | // [cell.replies.layer setCornerRadius:8];
88 | // [cell.userAvatar.layer setCornerRadius:cell.userAvatar.frame.size.width/5];
89 | //// cell.userAvatar.layer.masksToBounds = YES;
90 |
91 | NSUInteger row = [indexPath row];
92 | id rowData = [self.data objectAtIndex:row];
93 | [cell.title setText:[rowData valueForKey:@"title"]];
94 | [cell.nodeTitle setText:[[rowData objectForKey:@"node"] valueForKey:@"title"]];
95 |
96 | NSString *replies = [NSString stringWithFormat:@"%i",[[rowData valueForKey:@"replies"]intValue]];
97 | //Using [[rowData valueForKey:@"replies"] directly will lead to an exception.
98 | [cell.replies setText:replies];
99 |
100 | [cell.username setText:[[rowData objectForKey:@"member"] valueForKey:@"username"]];
101 | NSString *avatarUrl = [V2EXStringUtil hanldeAvatarURL:[[rowData objectForKey:@"member"] valueForKey:@"avatar_large"]];
102 | //TODO: Support https
103 | [cell.userAvatar setImageWithURL:[NSURL URLWithString:avatarUrl] placeholderImage:[UIImage imageNamed:@"avatar_large"]];
104 |
105 | return cell;
106 | }
107 |
108 | #pragma mark - UITableViewDelegate
109 |
110 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
111 | NSUInteger ID = [[[self.data objectAtIndex:indexPath.row] valueForKey:@"id"] intValue];
112 | _topicIDWillBePushedTo = ID;
113 | [self loadTopicData:ID];
114 | }
115 |
116 | - (void)requestDataSuccess:(id)dataObject {
117 | if (_loadingStatus == 1) {
118 | self.data = dataObject;
119 | } else if (_loadingStatus == 2) {
120 | // Push to new single topic controller
121 |
122 | [self pushToSingleTopicViewController:dataObject];
123 | }
124 |
125 | [super requestDataSuccess:dataObject];
126 | }
127 |
128 |
129 | //
130 | //- (void)requestDataFailure:(NSString *)errorMessage {
131 | // [self showMessage:errorMessage];
132 | // [self showProgressView];
133 | //}
134 |
135 |
136 | /*
137 | // Override to support rearranging the table view.
138 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
139 | {
140 | }
141 | */
142 |
143 | /*
144 | // Override to support conditional rearranging of the table view.
145 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
146 | {
147 | // Return NO if you do not want the item to be re-orderable.
148 | return YES;
149 | }
150 | */
151 |
152 | /*
153 | #pragma mark - Navigation
154 |
155 | // In a story board-based application, you will often want to do a little preparation before navigation
156 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
157 | {
158 | // Get the new view controller using [segue destinationViewController].
159 | // Pass the selected object to the new view controller.
160 | }
161 |
162 | */
163 |
164 | - (IBAction)showMenu:(id)sender {
165 | [self.sideMenuViewController presentMenuViewController];
166 | }
167 | @end
168 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMenuViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMenuViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RESideMenu.h"
11 | #import "UIViewController+MBProgressHUD.h"
12 |
13 | @interface V2EXMenuViewController : UIViewController
14 |
15 | @property (strong, readwrite, nonatomic) UITableView *tableView;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMenuViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMenuViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXMenuViewController.h"
10 | #import "V2EXAppDelegate.h"
11 | #import "V2EXUserLoginViewController.h"
12 | #import "UIViewController+MBProgressHUD.h"
13 |
14 | #define MENU_BUTTON_COUNT 4
15 |
16 | @interface V2EXMenuViewController ()
17 |
18 | @end
19 |
20 | @implementation V2EXMenuViewController
21 |
22 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
23 | {
24 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
25 | if (self) {
26 | // Custom initialization
27 | }
28 | return self;
29 | }
30 |
31 | - (void)viewDidLoad
32 | {
33 | [super viewDidLoad];
34 |
35 | self.tableView = ({
36 | UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, (self.view.frame.size.height - 54 * MENU_BUTTON_COUNT) / 2.0f, self.view.frame.size.width, 54 * MENU_BUTTON_COUNT) style:UITableViewStylePlain];
37 | tableView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth;
38 | tableView.delegate = self;
39 | tableView.dataSource = self;
40 | tableView.opaque = NO;
41 | tableView.backgroundColor = [UIColor clearColor];
42 |
43 | tableView.backgroundView = nil;
44 | tableView.backgroundColor = [UIColor clearColor];
45 | tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
46 | tableView.bounces = NO;
47 | tableView.scrollsToTop = NO;
48 | tableView;
49 | });
50 | [self.view addSubview:self.tableView];
51 | }
52 |
53 | - (void)didReceiveMemoryWarning
54 | {
55 | [super didReceiveMemoryWarning];
56 | // Dispose of any resources that can be recreated.
57 | }
58 |
59 | #pragma mark -
60 | #pragma mark UITableView Delegate
61 |
62 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
63 | {
64 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
65 | UINavigationController *navigationController = (UINavigationController *)self.sideMenuViewController.contentViewController;
66 |
67 | V2EXAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
68 |
69 | switch (indexPath.row) {
70 | case 0:
71 | // navigationController.viewControllers = @[[self.storyboard instantiateViewControllerWithIdentifier:@"nodesListController"]];
72 | navigationController.viewControllers = @[appDelegate.sharedNodesListViewController];
73 | [self.sideMenuViewController hideMenuViewController];
74 | break;
75 | case 1:
76 | navigationController.viewControllers = @[appDelegate.sharedLatestTopicsViewController];
77 | [self.sideMenuViewController hideMenuViewController];
78 | break;
79 | case 2:
80 | navigationController.viewControllers = @[appDelegate.sharedSelfViewController];
81 | [self.sideMenuViewController hideMenuViewController];
82 | break;
83 | case 3:
84 | /* navigationController.viewControllers = @[[self.storyboard instantiateViewControllerWithIdentifier:@"settingsController"]];
85 | [self.sideMenuViewController hideMenuViewController];
86 | break;
87 | case 4:*/
88 | [self doLogout];
89 | [appDelegate.sharedSelfViewController viewWillAppear:YES];
90 | break;
91 | default:
92 | break;
93 | }
94 | }
95 |
96 | #pragma mark -
97 | #pragma mark UITableView Datasource
98 |
99 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
100 | {
101 | return 54;
102 | }
103 |
104 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
105 | {
106 | return 1;
107 | }
108 |
109 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex
110 | {
111 | return MENU_BUTTON_COUNT;
112 | }
113 |
114 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
115 | {
116 | static NSString *cellIdentifier = @"Cell";
117 |
118 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
119 |
120 | if (cell == nil) {
121 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
122 | cell.backgroundColor = [UIColor clearColor];
123 | cell.textLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:21];
124 | cell.textLabel.textColor = [UIColor whiteColor];
125 | cell.textLabel.highlightedTextColor = [UIColor lightGrayColor];
126 | cell.selectedBackgroundView = [[UIView alloc] init];
127 | }
128 |
129 | NSArray *titles = @[@"社区节点", @"最新话题", @"个人中心",/* @"用户设置",*/ @"注销登录"];
130 | NSArray *images = @[@"IconHome", @"IconRecent", @"IconProfile", /*@"IconSettings",*/ @"IconKey"];
131 | cell.textLabel.text = titles[indexPath.row];
132 | cell.imageView.image = [UIImage imageNamed:images[indexPath.row]];
133 |
134 | return cell;
135 | }
136 |
137 | - (UIStatusBarStyle)preferredStatusBarStyle
138 | {
139 | return UIStatusBarStyleLightContent;
140 | }
141 |
142 | - (void)doLogout {
143 | [V2EXUserLoginViewController logout];
144 | NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
145 | [userDefaultes setObject:nil forKey:@"saved_username"];
146 | [userDefaultes setObject:nil forKey:@"saved_password"];
147 | [userDefaultes synchronize];
148 | [self showMessage:@"您已登出"];
149 | }
150 |
151 |
152 | #pragma mark -
153 | #pragma mark RESideMenu Delegate
154 |
155 | - (void)sideMenu:(RESideMenu *)sideMenu willShowMenuViewController:(UIViewController *)menuViewController
156 | {
157 | // NSLog(@"willShowMenuViewController");
158 | [self hideProgressView];
159 | }
160 |
161 | - (void)sideMenu:(RESideMenu *)sideMenu didShowMenuViewController:(UIViewController *)menuViewController
162 | {
163 | // NSLog(@"didShowMenuViewController");
164 | }
165 |
166 | - (void)sideMenu:(RESideMenu *)sideMenu willHideMenuViewController:(UIViewController *)menuViewController
167 | {
168 | // NSLog(@"willHideMenuViewController");
169 | }
170 |
171 | - (void)sideMenu:(RESideMenu *)sideMenu didHideMenuViewController:(UIViewController *)menuViewController
172 | {
173 | // NSLog(@"didHideMenuViewController");
174 | }
175 |
176 | @end
177 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMyRepliesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMyRepliesViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTableViewController.h"
10 | #import "TFHpple+V2EXMethod.h"
11 |
12 | @interface V2EXMyRepliesViewController : V2EXTableViewController
13 | {
14 | NSCache *_cellCache;
15 | NSMutableArray *_tid;
16 | NSString *_cssStyle;
17 |
18 | NSMutableArray *_topicLinkArray;
19 | }
20 |
21 | @property (strong, nonatomic) TFHpple *doc;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMyRepliesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMyRepliesViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXMyRepliesViewController.h"
10 | #import
11 | #import "V2EXStringUtil.h"
12 | #define AttributedTextCellReuseIdentifier @"userRepliesListCell"
13 |
14 | @interface V2EXMyRepliesViewController ()
15 |
16 | @end
17 |
18 | @implementation V2EXMyRepliesViewController
19 |
20 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
21 | {
22 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
23 | if (self) {
24 | // Custom initialization
25 | }
26 | return self;
27 | }
28 |
29 | - (void)viewDidLoad
30 | {
31 | [super viewDidLoad];
32 | // Do any additional setup after loading the view.
33 |
34 | NSBundle *bundle = [NSBundle mainBundle];
35 | NSString *path = [bundle pathForResource:@"repliesList" ofType:@"css"];
36 | NSData *cssData = [NSData dataWithContentsOfFile:path];
37 | _cssStyle = [[@""];
38 |
39 | _cellCache = [NSCache new];
40 | [self handleRepliesData];
41 |
42 | }
43 |
44 | - (void)handleRepliesData {
45 | self.data = [[NSMutableArray alloc] init];
46 | _topicLinkArray = [[NSMutableArray alloc] init];
47 |
48 | NSArray *repliesDockArray = [self.doc searchWithXPathQuery:@"//div[@class='dock_area']"];
49 | NSArray *repliesInnerArray = [self.doc searchWithXPathQuery:@"//div[@class='reply_content']"];
50 | for (int i = 0; i < [repliesDockArray count]; ++i) {
51 | NSString *row = [[[repliesDockArray objectAtIndex:i] raw] stringByAppendingString:[[repliesInnerArray objectAtIndex:i] raw]];
52 | [self.data addObject:row];
53 |
54 | NSString *link = [[[repliesDockArray objectAtIndex:i] searchFirstElementWithXPathQuery:@"//span[@class='gray']/a"] objectForKey:@"href"];
55 | [_topicLinkArray addObject:link?:@""];
56 | }
57 | }
58 |
59 | #pragma mark UITableViewDataSource
60 | - (void)configureCell:(DTAttributedTextCell *)cell forIndexPath:(NSIndexPath *)indexPath
61 | {
62 | NSDictionary *rowData = [self.data objectAtIndex:indexPath.row];
63 |
64 | NSString *formattedContent = [_cssStyle stringByAppendingString:[NSString stringWithFormat:@"%@
", rowData]];
65 | [cell setHTMLString:formattedContent];
66 |
67 | cell.accessoryType = UITableViewCellAccessoryNone;
68 | cell.attributedTextContextView.shouldDrawImages = YES;
69 | }
70 |
71 | - (BOOL)_canReuseCells
72 | {
73 | // reuse does not work for variable height
74 |
75 | if ([self respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)])
76 | {
77 | return NO;
78 | }
79 |
80 | // only reuse cells with fixed height
81 | return YES;
82 | }
83 |
84 | - (DTAttributedTextCell *)tableView:(UITableView *)tableView preparedCellForIndexPath:(NSIndexPath *)indexPath
85 | {
86 | // workaround for iOS 5 bug
87 | NSString *key = [NSString stringWithFormat:@"%ld-%ld", (long)indexPath.section, (long)indexPath.row];
88 |
89 | DTAttributedTextCell *cell = [_cellCache objectForKey:key];
90 |
91 | if (!cell)
92 | {
93 | if ([self _canReuseCells])
94 | {
95 | cell = (DTAttributedTextCell *)[tableView dequeueReusableCellWithIdentifier:AttributedTextCellReuseIdentifier];
96 | }
97 |
98 | if (!cell)
99 | {
100 | cell = [[DTAttributedTextCell alloc] initWithReuseIdentifier:AttributedTextCellReuseIdentifier];
101 | }
102 |
103 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
104 | cell.hasFixedRowHeight = NO;
105 |
106 | // cache it, if there is a cache
107 | [_cellCache setObject:cell forKey:key];
108 | }
109 |
110 | [self configureCell:cell forIndexPath:indexPath];
111 |
112 | return cell;
113 | }
114 |
115 | // disable this method to get static height = better performance
116 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
117 | {
118 | DTAttributedTextCell *cell = (DTAttributedTextCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
119 |
120 | return [cell requiredRowHeightInTableView:tableView];
121 | }
122 |
123 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
124 | {
125 | DTAttributedTextCell *cell = (DTAttributedTextCell *)[self tableView:tableView preparedCellForIndexPath:indexPath];
126 |
127 | return cell;
128 | }
129 |
130 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
131 | NSUInteger index = [indexPath row];
132 | NSUInteger topicID = [V2EXStringUtil link2TopicID:[_topicLinkArray objectAtIndex:index]];
133 |
134 | if (topicID > 0) {
135 | [self.model getTopicWithID:topicID];
136 | [self showProgressView];
137 | } else {
138 | [self showMessage:@"无法加载您的主题"];
139 | }
140 | }
141 |
142 | - (void)requestDataSuccess:(id)dataObject {
143 | [self pushToSingleTopicViewController:dataObject];
144 | }
145 |
146 |
147 | @end
148 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMyTopicsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMyTopicsViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTableViewController.h"
10 | #import "TFHpple+V2EXMethod.h"
11 |
12 | @interface V2EXMyTopicsViewController : V2EXTableViewController
13 |
14 | @property (strong, nonatomic) TFHpple *doc;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXMyTopicsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMyTopicsViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXMyTopicsViewController.h"
10 | #import "UIViewController+MBProgressHUD.h"
11 | #import "V2EXUserTopicsListCell.h"
12 | #import "V2EXStringUtil.h"
13 |
14 | @interface V2EXMyTopicsViewController ()
15 |
16 | @end
17 |
18 | @implementation V2EXMyTopicsViewController
19 |
20 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
21 | {
22 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
23 | if (self) {
24 | // Custom initialization
25 | }
26 | return self;
27 | }
28 |
29 | - (void)viewDidLoad
30 | {
31 | [super viewDidLoad];
32 |
33 | if (self.doc == nil) {
34 | [self showMessage:@"未知错误,无法加载您的主题"];
35 | return;
36 | }
37 | self.tableView.rowHeight = 70; // TODO: Why don't storyboard with identifiertopicListInSingleNodeController support rowHeight?
38 | [self handleUserTopicsListData];
39 |
40 | }
41 |
42 | - (void)handleUserTopicsListData {
43 | self.data = [[NSMutableArray alloc] init];
44 | NSArray *topicsArray = [self.doc searchWithXPathQuery:@"//div[@class='content']/div[@class='box']/div[@class='cell item']"];
45 |
46 | for (TFHppleElement *element in topicsArray) {
47 | NSString *title = [[element searchFirstElementWithXPathQuery:@"//span[@class='item_title']/a"] text]?:@"";
48 | NSString *nodeTitle = [[element searchFirstElementWithXPathQuery:@"//span[@class='small fade']/a[1]"] text]?:@"";
49 | NSString *replies = [[element searchFirstElementWithXPathQuery:@"//a[@class='count_orange']"] text]?:@"0";
50 | NSString *lastReplyUsername = [[element searchFirstElementWithXPathQuery:@"//span[@class='small fade']/strong[2]/a"] text]?:@"无回复";
51 | NSString *link = [[element searchFirstElementWithXPathQuery:@"//span[@class='item_title']/a"] objectForKey:@"href"];
52 |
53 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
54 | title, @"title",
55 | nodeTitle, @"nodeTitle",
56 | replies, @"replies",
57 | lastReplyUsername, @"lastReplyUsername",
58 | link, @"link",
59 | nil];
60 | [self.data addObject:dict];
61 | }
62 | }
63 |
64 | - (void)didReceiveMemoryWarning
65 | {
66 | [super didReceiveMemoryWarning];
67 | // Dispose of any resources that can be recreated.
68 | }
69 |
70 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
71 | {
72 | NSUInteger index = [indexPath row];
73 | static NSString *CellIdentifier = @"userTopicListCell";
74 |
75 | UINib *nib = [UINib nibWithNibName:@"V2EXUserTopicsListCell" bundle:nil];
76 | [tableView registerNib:nib forCellReuseIdentifier:CellIdentifier];
77 |
78 | V2EXUserTopicsListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
79 |
80 | NSDictionary *rowData = [self.data objectAtIndex:index];
81 |
82 | cell.title.text = [rowData valueForKey:@"title"];
83 | cell.nodeTitle.text = [rowData valueForKey:@"nodeTitle"];
84 | cell.replies.text = [rowData valueForKey:@"replies"];
85 | cell.lastReplyUsername.text = [rowData valueForKey:@"lastReplyUsername"];
86 |
87 | return cell;
88 | }
89 |
90 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
91 | NSUInteger index = [indexPath row];
92 | NSUInteger topicID = [V2EXStringUtil link2TopicID:[[self.data objectAtIndex:index] objectForKey:@"link"]];
93 |
94 | if (topicID > 0) {
95 | [self.model getTopicWithID:topicID];
96 | [self showProgressView];
97 | } else {
98 | [self showMessage:@"无法加载您的主题"];
99 | }
100 | }
101 |
102 | - (void)requestDataSuccess:(id)dataObject {
103 | [self pushToSingleTopicViewController:dataObject];
104 | }
105 |
106 |
107 |
108 | @end
109 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXNewTopicViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNewTopicViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/8/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "UIViewController+MBProgressHUD.h"
11 | #import "V2EXRequestDataDelegate.h"
12 | #import "V2EXNormalModel.h"
13 | #import "V2EXAfterTopicActionDelegate.h"
14 |
15 | @interface V2EXNewTopicViewController : UIViewController
16 | {
17 | CGRect _originalTextViewFrame;
18 |
19 | NSUInteger _once;
20 | NSUInteger _lastRequestTime;
21 |
22 | V2EXNormalModel *_model;
23 | }
24 |
25 | @property (nonatomic, assign) id lastController;
26 | @property (strong, nonatomic) IBOutlet UITextField *topicTitle;
27 | @property (strong, nonatomic) IBOutlet UITextView *topicContent;
28 | @property (strong, nonatomic) IBOutlet UIBarButtonItem *rightButton;
29 | @property (strong, nonatomic) NSString *uri;
30 |
31 | - (IBAction)newTopicButtonClick:(id)sender;
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXNewTopicViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNewTopicViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/8/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXNewTopicViewController.h"
10 | #import "TFHpple+V2EXMethod.h"
11 |
12 | @interface V2EXNewTopicViewController ()
13 |
14 | @end
15 |
16 | @implementation V2EXNewTopicViewController
17 |
18 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
19 | {
20 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
21 | if (self) {
22 | // Custom initialization
23 | }
24 | return self;
25 | }
26 |
27 | - (void)viewDidLoad
28 | {
29 | [super viewDidLoad];
30 |
31 | [self configureTextViewLayout];
32 |
33 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
34 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
35 | _originalTextViewFrame = self.topicContent.frame;
36 |
37 | _model = [[V2EXNormalModel alloc] initWithDelegate:self];
38 | }
39 |
40 | - (void)viewWillAppear:(BOOL)animated {
41 | [self request4OnceCode];
42 | }
43 |
44 |
45 | - (void)didReceiveMemoryWarning
46 | {
47 | [super didReceiveMemoryWarning];
48 | // Dispose of any resources that can be recreated.
49 | }
50 |
51 | - (void)configureTextViewLayout {
52 | [self.topicContent.layer setBorderColor:[[[UIColor lightGrayColor] colorWithAlphaComponent:0.3] CGColor]];
53 | [self.topicContent.layer setBorderWidth:1.0];
54 |
55 | self.topicContent.layer.cornerRadius = 5;
56 | self.topicContent.clipsToBounds = YES;
57 | }
58 |
59 | - (void)keyboardWillShow:(NSNotification *)notification {
60 | [self moveTextViewForKeyboard:notification up:YES];
61 | }
62 |
63 | - (void)keyboardWillHide:(NSNotification *)notification {
64 | [self moveTextViewForKeyboard:notification up:NO];
65 | }
66 |
67 | - (void)moveTextViewForKeyboard:(NSNotification*)notification up:(BOOL)up {
68 | NSDictionary *userInfo = [notification userInfo];
69 | NSTimeInterval animationDuration;
70 | UIViewAnimationCurve animationCurve;
71 | CGRect keyboardRect;
72 |
73 | [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
74 | animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
75 | keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
76 | keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
77 |
78 | [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
79 | [UIView setAnimationDuration:animationDuration];
80 | [UIView setAnimationCurve:animationCurve];
81 |
82 | if (up == YES) {
83 | CGFloat keyboardTop = keyboardRect.origin.y;
84 | CGRect newTextViewFrame = self.topicContent.frame;
85 | // originalTextViewFrame = self.replyContent.frame;
86 | newTextViewFrame.size.height = keyboardTop - self.topicContent.frame.origin.y - 5;
87 |
88 | self.topicContent.frame = newTextViewFrame;
89 | } else {
90 | // Keyboard is going away (down) - restore original frame
91 | self.topicContent.frame = _originalTextViewFrame;
92 | }
93 |
94 | [UIView commitAnimations];
95 | }
96 |
97 | - (IBAction)newTopicButtonClick:(id)sender {
98 | [self showProgressView];
99 | if ((_lastRequestTime - [[NSDate date] timeIntervalSince1970]) >= 900
100 | || _once <= 0) {
101 | [self request4OnceCode];
102 | } else {
103 | [self request2PostNewTopic];
104 | }
105 | }
106 |
107 | - (void)request4OnceCode {
108 | if ([self.uri length] <= 0) {
109 | [self showMessage:@"意外错误,请重试"];
110 | [self.navigationController popViewControllerAnimated:YES];
111 | return;
112 | }
113 | [_model getNewTopicPage:self.uri];
114 | }
115 |
116 | - (void)request2PostNewTopic {
117 | if ([self.topicTitle.text length] <= 0) {
118 | [self showMessage:@"标题不能为空"];
119 | return;
120 | }
121 |
122 | self.rightButton.enabled = NO;
123 |
124 | NSLog(@"Posting new topic: %@, to node: %@", self.topicTitle.text, self.uri);
125 | [_model newTopic:self.uri andTitle:self.topicTitle.text andContent:self.topicContent.text andOnce:_once];
126 | }
127 |
128 | - (void)requestDataSuccess:(id)dataObject {
129 | if (_once <= 0) {
130 | [self hanldeOnceCode:dataObject];
131 | if (self.rightButton.enabled == NO) {
132 | [self request2PostNewTopic];
133 | }
134 | } else {
135 | [self handlePostRespondData:dataObject];
136 | }
137 | }
138 |
139 | - (void)hanldeOnceCode:(NSData *)data {
140 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:data];
141 | NSString *onceCode = [[doc searchFirstElementWithXPathQuery:@"//input[@name='once']"] objectForKey:@"value"];
142 | _once = (NSUInteger)[onceCode integerValue];
143 | }
144 |
145 | - (void)handlePostRespondData:(NSData *)data {
146 | self.rightButton.enabled = YES;
147 | [self hideProgressView];
148 | [self.navigationController popViewControllerAnimated:NO];
149 |
150 |
151 | // TODO: Validation for new topic
152 | // TFHpple *doc = [[TFHpple alloc] initWithHTMLData:data];
153 | // NSString *newTopicTitle = [[doc searchFirstElementWithXPathQuery:@"//div[@class='header']/h1"] text];
154 | // if (newTopicTitle == self.topicTitle.text) {
155 |
156 | // BOOL isSuccess = [[@" " stringByAppendingString:newTopicTitle] stringByAppendingString:@" "] == self.topicTitle.text;
157 | [self.lastController afterCreateTopic:data];
158 | NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
159 | // }
160 | }
161 |
162 | - (void)requestDataFailure:(NSString *)errorMessage {
163 | _once = 0;
164 | self.rightButton.enabled = YES;
165 | }
166 |
167 | - (void)setOnceValue:(NSUInteger)once {
168 | _once = once;
169 | _lastRequestTime = [[NSDate date] timeIntervalSince1970];
170 | }
171 |
172 |
173 | @end
174 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXNodesListViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNodesListViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | #import "DRPaginatedScrollView.h"
13 | #import "V2EXNodesListModel.h"
14 | #import "V2EXRequestDataDelegate.h"
15 | #import "UIViewController+MBProgressHUD.h"
16 | #import "V2EXNormalModel.h"
17 | #import "V2EXTopicsListInSingleNodeViewController.h"
18 |
19 |
20 | @interface V2EXNodesListViewController : UIViewController
21 | {
22 | CGFloat _segmentedControlX;
23 | V2EXNormalModel *_normalModel;
24 |
25 | NSMutableString *_uriClicked;
26 |
27 | NSMutableString *_loadingNodeTitle;
28 | }
29 |
30 | @property (strong, nonatomic) DRPaginatedScrollView * paginatedScrollView;
31 | @property (strong, nonatomic) HMSegmentedControl *segmentedControl;
32 |
33 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel0;
34 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel1;
35 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel2;
36 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel3;
37 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel4;
38 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel5;
39 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel6;
40 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel7;
41 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel8;
42 | @property (nonatomic, retain) V2EXNodesListModel *nodesListModel9;
43 |
44 | + (V2EXNodesListViewController *)sharedController;
45 | - (IBAction)showMenu:(id)sender;
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXNotificationCenterViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNotificationCenterViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTableViewController.h"
10 |
11 | @interface V2EXNotificationCenterViewController : V2EXTableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXNotificationCenterViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNotificationCenterViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/20/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXNotificationCenterViewController.h"
10 |
11 | @interface V2EXNotificationCenterViewController ()
12 |
13 | @end
14 |
15 | @implementation V2EXNotificationCenterViewController
16 |
17 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
18 | {
19 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
20 | if (self) {
21 | // Custom initialization
22 | }
23 | return self;
24 | }
25 |
26 | - (void)viewDidLoad
27 | {
28 | [super viewDidLoad];
29 | // Do any additional setup after loading the view.
30 | }
31 |
32 | - (void)didReceiveMemoryWarning
33 | {
34 | [super didReceiveMemoryWarning];
35 | // Dispose of any resources that can be recreated.
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXReplyTopicViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXReplyTopicViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/5/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "V2EXNormalModel.h"
11 | #import "V2EXAfterTopicActionDelegate.h"
12 |
13 | @interface V2EXReplyTopicViewController : UIViewController
14 | {
15 | V2EXNormalModel *_model;
16 |
17 | CGRect _originalTextViewFrame;
18 | }
19 |
20 | @property (nonatomic, assign) id lastController;
21 | @property (strong, nonatomic) IBOutlet UITextView *replyContent;
22 | @property (nonatomic) NSUInteger topicID;
23 | @property (nonatomic) NSUInteger onceCode;
24 |
25 | - (IBAction)doReply:(id)sender;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXReplyTopicViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXReplyTopicViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/5/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXReplyTopicViewController.h"
10 | #import "UIViewController+MBProgressHUD.h"
11 | #import "TFHpple+V2EXMethod.h"
12 |
13 | @interface V2EXReplyTopicViewController ()
14 |
15 | @end
16 |
17 | @implementation V2EXReplyTopicViewController
18 |
19 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
20 | {
21 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
22 | if (self) {
23 | // Custom initialization
24 | }
25 | return self;
26 | }
27 |
28 | - (void)viewDidLoad
29 | {
30 | [super viewDidLoad];
31 |
32 | if (self.topicID <= 0 || self.onceCode <= 0) {
33 | [self showMessage:@"您尚未登录,不能回复"];
34 | [self.navigationController popViewControllerAnimated:YES];
35 | return;
36 | }
37 |
38 | [self configureTextViewLayout];
39 |
40 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
41 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
42 | _originalTextViewFrame = self.replyContent.frame;
43 |
44 | _model = [[V2EXNormalModel alloc] initWithDelegate:self];
45 | }
46 |
47 | - (void)viewDidAppear:(BOOL)animated {
48 | // [self.replyContent becomeFirstResponder];
49 | }
50 |
51 | - (void)keyboardWillShow:(NSNotification *)notification {
52 | [self moveTextViewForKeyboard:notification up:YES];
53 | }
54 |
55 | - (void)keyboardWillHide:(NSNotification *)notification {
56 | [self moveTextViewForKeyboard:notification up:NO];
57 | }
58 |
59 | - (void)moveTextViewForKeyboard:(NSNotification*)notification up:(BOOL)up {
60 | NSDictionary *userInfo = [notification userInfo];
61 | NSTimeInterval animationDuration;
62 | UIViewAnimationCurve animationCurve;
63 | CGRect keyboardRect;
64 |
65 | [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
66 | animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
67 | keyboardRect = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
68 | keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
69 |
70 | [UIView beginAnimations:@"ResizeForKeyboard" context:nil];
71 | [UIView setAnimationDuration:animationDuration];
72 | [UIView setAnimationCurve:animationCurve];
73 |
74 | if (up == YES) {
75 | CGFloat keyboardTop = keyboardRect.origin.y;
76 | CGRect newTextViewFrame = self.replyContent.frame;
77 | // originalTextViewFrame = self.replyContent.frame;
78 | newTextViewFrame.size.height = keyboardTop - self.replyContent.frame.origin.y - 5;
79 |
80 | self.replyContent.frame = newTextViewFrame;
81 | } else {
82 | // Keyboard is going away (down) - restore original frame
83 | self.replyContent.frame = _originalTextViewFrame;
84 | }
85 |
86 | [UIView commitAnimations];
87 | }
88 |
89 | - (void)requestDataSuccess:(id)dataObject {
90 | [self hideProgressView];
91 |
92 | TFHppleElement *replyFormElement = [[[TFHpple alloc] initWithHTMLData:dataObject] searchFirstElementWithXPathQuery:@"//div[@class='box']//div[@class='cell']//form"];
93 |
94 | if (replyFormElement) {
95 | NSUInteger responseTopicID = (NSUInteger)[[[replyFormElement objectForKey:@"action"] stringByReplacingOccurrencesOfString:@"/t/" withString:@""] integerValue];
96 | if (responseTopicID == self.topicID && responseTopicID > 0) {
97 | [self.lastController afterReplyTopic:dataObject];
98 | [self showMessage:@"回复成功"];
99 | [self.navigationController popViewControllerAnimated:YES];
100 | return;
101 | }
102 | }
103 |
104 | [self showMessage:@"回复失败,请重试"];
105 | return;
106 | }
107 |
108 | - (IBAction)doReply:(id)sender {
109 | if ([self.replyContent.text length] > 0) {
110 | [self showProgressView];
111 | [_model replyTopic:self.topicID andOnce:self.onceCode andContent:self.replyContent.text];
112 | } else {
113 | [self showMessage:@"回复内容不能为空"];
114 | }
115 | }
116 |
117 | - (void)configureTextViewLayout {
118 | [self.replyContent.layer setBorderColor:[[[UIColor lightGrayColor] colorWithAlphaComponent:0.3] CGColor]];
119 | [self.replyContent.layer setBorderWidth:1.0];
120 |
121 | self.replyContent.layer.cornerRadius = 5;
122 | self.replyContent.clipsToBounds = YES;
123 | }
124 |
125 |
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXRootViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RESideMenu.h"
11 |
12 | @interface V2EXRootViewController : RESideMenu
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXRootViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXRootViewController.h"
10 | #import "V2EXMenuViewController.h"
11 |
12 | @interface V2EXRootViewController ()
13 |
14 | @end
15 |
16 | @implementation V2EXRootViewController
17 |
18 | - (void)awakeFromNib
19 | {
20 | self.contentViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"contentController"];
21 | // The first sub view controller is according to contentController
22 |
23 | self.menuViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"menuController"];
24 | self.backgroundImage = [UIImage imageNamed:@"Stars"];
25 | self.delegate = (V2EXMenuViewController *)self.menuViewController;
26 | }
27 |
28 | - (void)viewDidLoad
29 | {
30 | [super viewDidLoad];
31 | // Do any additional setup after loading the view, typically from a nib.
32 | }
33 |
34 | - (void)didReceiveMemoryWarning
35 | {
36 | [super didReceiveMemoryWarning];
37 | // Dispose of any resources that can be recreated.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXSelfViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXSelfViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/1/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RESideMenu.h"
11 | #import "V2EXNormalModel.h"
12 | #import
13 |
14 | @interface V2EXSelfViewController : UIViewController
15 | {
16 | NSUInteger _loadingStatus; // 0 - No loading ; 1 - Loading my topics ; 2 - Loading my replies ; 3 - Loading notification ; 4 - Self infomation
17 | NSTimeInterval _lastLoadUserInformationTime;
18 | V2EXNormalModel *_model;
19 |
20 | NSMutableString *_currentUsername;
21 | }
22 | @property (strong, nonatomic) IBOutlet UILabel *usernameLabel;
23 | @property (strong, nonatomic) IBOutlet UIImageView *avatar;
24 | @property (strong, nonatomic) IBOutlet UILabel *userMetaLabel;
25 | //@property (strong, nonatomic) IBOutlet DTAttributedLabel *userSocialInformationLabel;
26 | @property (strong, nonatomic) IBOutlet UIButton *myTopicsButton;
27 | @property (strong, nonatomic) IBOutlet UIButton *myRepliesButton;
28 | @property (strong, nonatomic) IBOutlet UIButton *notificationCenterButton;
29 |
30 | + (V2EXSelfViewController *)sharedController;
31 | - (IBAction)showMenu:(id)sender;
32 | - (IBAction)myTopicsButtonClick:(id)sender;
33 | - (IBAction)myRepliesButtonClick:(id)sender;
34 | - (IBAction)notificationCenterButtonClick:(id)sender;
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXSelfViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXSelfViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/1/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXSelfViewController.h"
10 | #import "V2EXUserLoginViewController.h"
11 | #import "UIViewController+MBProgressHUD.h"
12 | #import "TFHpple+V2EXMethod.h"
13 | #import
14 | #import
15 |
16 | #import "V2EXMyTopicsViewController.h"
17 | #import "V2EXMyRepliesViewController.h"
18 | #import "V2EXNotificationCenterViewController.h"
19 |
20 | @interface V2EXSelfViewController ()
21 |
22 | @end
23 |
24 | @implementation V2EXSelfViewController
25 |
26 | + (V2EXSelfViewController *)sharedController
27 | {
28 | static V2EXSelfViewController *_sharedLatestTopicsViewControllerInstance = nil;
29 | static dispatch_once_t predicate;
30 | dispatch_once(&predicate, ^{
31 | _sharedLatestTopicsViewControllerInstance = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"selfController"];
32 | });
33 |
34 | return _sharedLatestTopicsViewControllerInstance;
35 | }
36 |
37 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
38 | {
39 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
40 | if (self) {
41 | // Custom initialization
42 | }
43 | return self;
44 | }
45 |
46 | - (void)viewDidLoad
47 | {
48 | [super viewDidLoad];
49 | _model = [[V2EXNormalModel alloc] initWithDelegate:self];
50 | _lastLoadUserInformationTime = 0;
51 | }
52 |
53 | - (void)viewWillAppear:(BOOL)animated {
54 | [self prepareForUserInfo];
55 | }
56 |
57 | - (void)prepareForUserInfo {
58 | NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
59 | if (![[[userDefaultes stringForKey:@"saved_username"] uppercaseString] isEqualToString:[_currentUsername uppercaseString]]) {
60 | _lastLoadUserInformationTime = 0;
61 | NSLog(@"'%@' set '%@'", [[userDefaultes stringForKey:@"saved_username"] uppercaseString], [_currentUsername uppercaseString]);
62 | }
63 | _currentUsername = [[userDefaultes stringForKey:@"saved_username"] mutableCopy];
64 |
65 | if (([[NSDate date] timeIntervalSince1970] - _lastLoadUserInformationTime) > 86400) {
66 | // TODO: Produce more viable way to decide if reload data
67 | _loadingStatus = 4;
68 |
69 | if (_currentUsername) {
70 | [_model getUserInfo:_currentUsername];
71 | [self showProgressView];
72 | } else {
73 | [self pushToUserLoginController];
74 | }
75 | }
76 | }
77 |
78 | - (void)pushToUserLoginController {
79 | [self showMessage:@"您尚未登录,请登录"];
80 | V2EXUserLoginViewController *userLoginController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"userLoginController"];
81 | userLoginController.isLoadFromSeflController = YES;
82 | [self.navigationController pushViewController:userLoginController animated:YES];
83 | }
84 |
85 |
86 | - (void)requestDataSuccess:(id)dataObject {
87 | switch (_loadingStatus) {
88 | case 1:
89 | {
90 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:dataObject];
91 | V2EXMyTopicsViewController *myTopicsController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"myTopicsController"];
92 | [myTopicsController setDoc:doc];
93 | [self.navigationController pushViewController:myTopicsController animated:YES];
94 | }
95 | break;
96 | case 2:
97 | {
98 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:dataObject];
99 | V2EXMyRepliesViewController *myTopicsController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"myRepliesController"];
100 | [myTopicsController setDoc:doc];
101 | [self.navigationController pushViewController:myTopicsController animated:YES];
102 | }
103 | break;
104 | case 4:
105 | {
106 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:dataObject];
107 | if ([doc checkLogin]) {
108 | [self handleWithUserPage:doc];
109 | } else {
110 | [self pushToUserLoginController];
111 | }
112 | }
113 | break;
114 | default:
115 | break;
116 | }
117 |
118 | [self enableAllButtons];
119 | }
120 |
121 |
122 |
123 | - (void)requestDataFailure:(NSString *)errorMessage {
124 | [self enableAllButtons];
125 | }
126 |
127 | - (void)handleWithUserPage:(TFHpple *)doc {
128 | _lastLoadUserInformationTime = [[NSDate date] timeIntervalSince1970];
129 |
130 | self.usernameLabel.text = [self getValueForDoc:doc withXpath:@"//div[@class='inner']/table//tr/td[3]/h1"];
131 |
132 | NSURL *avatarURL = [NSURL URLWithString:[@"https:" stringByAppendingString:[[doc searchFirstElementWithXPathQuery:@"//img[@class='avatar']"] objectForKey:@"src"]]];
133 | // TODO: https
134 | [self.avatar setImageWithURL:avatarURL placeholderImage:[UIImage imageNamed:@"avatar_large"]];
135 |
136 | self.userMetaLabel.text = [[self getValueForDoc:doc withXpath:@"//table//td[@width='auto']//span[@class='gray']"] stringByReplacingOccurrencesOfString:@"V2EX " withString:@""];
137 |
138 | // NSString *socialInformationHTML = [[[doc searchFirstElementWithXPathQuery:@"//div[@class='inner']"] raw] stringByReplacingOccurrencesOfString:@" src=\"" withString:@"style=\"color:black;\" src=\"http://v2ex.com"];
139 | // self.userSocialInformationLabel.attributedString = [[NSAttributedString alloc] initWithHTMLData:[socialInformationHTML dataUsingEncoding:NSUTF8StringEncoding] documentAttributes:NULL];
140 |
141 | }
142 |
143 | - (NSString *)getValueForDoc:(TFHpple *)doc withXpath:(NSString *)xpath {
144 | return [[doc searchFirstElementWithXPathQuery:xpath] text];
145 | }
146 |
147 | // Next controllers:
148 |
149 | - (IBAction)showMenu:(id)sender {
150 | [self.sideMenuViewController presentMenuViewController];
151 | }
152 |
153 | - (IBAction)myTopicsButtonClick:(id)sender {
154 | [self disableAllButtons];
155 | _loadingStatus = 1;
156 | [_model getUserTopics:_currentUsername];
157 | }
158 |
159 | - (IBAction)myRepliesButtonClick:(id)sender {
160 | [self disableAllButtons];
161 | _loadingStatus = 2;
162 | [_model getUserReplies:_currentUsername];
163 | }
164 |
165 | - (IBAction)notificationCenterButtonClick:(id)sender {
166 | [self disableAllButtons];
167 | _loadingStatus = 3;
168 | NSLog(@"Nothing 2 do");
169 | }
170 |
171 | - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
172 | return NO;
173 | }
174 |
175 | - (void)disableAllButtons {
176 | self.myTopicsButton.enabled = self.myRepliesButton.enabled = self.notificationCenterButton.enabled = NO;
177 | [self showProgressView];
178 | }
179 |
180 | - (void)enableAllButtons {
181 | _loadingStatus = 0;
182 | self.myTopicsButton.enabled = self.myRepliesButton.enabled = self.notificationCenterButton.enabled = YES;
183 | // [self hideProgressView];
184 | }
185 |
186 | @end
187 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXSettingsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXSettingsViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RESideMenu.h"
11 |
12 | @interface V2EXSettingsViewController : UIViewController
13 |
14 | + (V2EXSettingsViewController *)sharedController;
15 | - (IBAction)showMenu:(id)sender;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXSettingsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXSettingsViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXSettingsViewController.h"
10 | #import
11 |
12 | @interface V2EXSettingsViewController ()
13 |
14 | @end
15 |
16 | @implementation V2EXSettingsViewController
17 |
18 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
19 | {
20 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
21 | if (self) {
22 | // Custom initialization
23 | }
24 | return self;
25 | }
26 |
27 | - (void)viewDidLoad
28 | {
29 | [super viewDidLoad];
30 | // Do any additional setup after loading the view.
31 |
32 | V2EXNormalModel *model = [[V2EXNormalModel alloc] initWithDelegate:self];
33 | [model getIndex];
34 | }
35 |
36 | - (void)requestDataSuccess:(id)dataObject {
37 | NSLog(@"%@", [[NSString alloc] initWithData:dataObject encoding:NSUTF8StringEncoding]);
38 | }
39 |
40 | - (void)didReceiveMemoryWarning
41 | {
42 | [super didReceiveMemoryWarning];
43 | // Dispose of any resources that can be recreated.
44 | }
45 |
46 | + (V2EXSettingsViewController *)sharedController
47 | {
48 | static V2EXSettingsViewController *_sharedSettingsViewControllerInstance = nil;
49 | static dispatch_once_t predicate;
50 | dispatch_once(&predicate, ^{
51 | _sharedSettingsViewControllerInstance = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"settingsController"];
52 | });
53 |
54 | return _sharedSettingsViewControllerInstance;
55 | }
56 |
57 | - (IBAction)showMenu:(id)sender {
58 | [self.sideMenuViewController presentMenuViewController];
59 | }
60 | @end
61 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXSingleTopicViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXSingleTopicViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/19/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTableViewController.h"
10 | #import "V2EXAfterTopicActionDelegate.h"
11 |
12 | @interface V2EXSingleTopicViewController : V2EXTableViewController
13 | {
14 | // NSMutableArray *_cellHeightArray;
15 | NSUInteger _topicID;
16 | NSUInteger _onceCode;
17 | NSCache *_cellCache;
18 |
19 | NSUInteger _modelStatus; // 1 - topic; 2 - report
20 | }
21 |
22 | + (V2EXSingleTopicViewController *)sharedController;
23 |
24 | - (void)loadNewTopicWithID:(NSUInteger)ID;
25 | - (void)loadNewTopicWithData:(NSData *)data;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXTableViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTableViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/7/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "V2EXRequestDataDelegate.h"
12 | #import "UIViewController+MBProgressHUD.h"
13 | #import "V2EXNormalModel.h"
14 |
15 | @interface V2EXTableViewController : UITableViewController
16 | {
17 | NSUInteger _loadingStatus; // 0 - No loading ; 1 - Loading topic list ; 2 - Loading single topic
18 | NSUInteger _topicIDWillBePushedTo;
19 | }
20 |
21 | @property (nonatomic, strong) NSMutableArray* data;
22 | @property (nonatomic, strong) V2EXNormalModel *model;
23 |
24 | - (BOOL)canStartNewLoading;
25 | - (void)pushToSingleTopicViewController:(NSData *)dataObject;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXTableViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTableViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/7/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTableViewController.h"
10 | #import "V2EXSingleTopicViewController.h"
11 | #import "TFHpple+V2EXMethod.h"
12 | #import "V2EXUserLoginViewController.h"
13 | #import "UIViewController+V2EXJump.h"
14 |
15 | @interface V2EXTableViewController ()
16 |
17 | @end
18 |
19 | @implementation V2EXTableViewController
20 |
21 | - (id)initWithStyle:(UITableViewStyle)style
22 | {
23 | self = [super initWithStyle:style];
24 | if (self) {
25 | // Custom initialization
26 | }
27 | return self;
28 | }
29 |
30 | - (void)viewDidLoad
31 | {
32 | [super viewDidLoad];
33 |
34 | // Uncomment the following line to preserve selection between presentations.
35 | // self.clearsSelectionOnViewWillAppear = NO;
36 |
37 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
38 | // self.navigationItem.rightBarButtonItem = self.editButtonItem;
39 |
40 | // Refresh control
41 | UIRefreshControl *rc = [[UIRefreshControl alloc] init];
42 | rc.attributedTitle = [[NSAttributedString alloc] initWithString:@"下拉刷新"];
43 | [rc addTarget:self action:@selector(refreshTableView) forControlEvents:UIControlEventValueChanged];
44 | self.refreshControl = rc;
45 | self.tableView.separatorInset = UIEdgeInsetsZero;
46 |
47 | // Data model
48 | if(!_model){
49 | _model = [[V2EXNormalModel alloc]initWithDelegate:self];
50 | }
51 | }
52 |
53 |
54 | - (void)refreshTableView {
55 | if(self.refreshControl.refreshing){
56 | self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"加载中"];
57 | [self loadData];
58 | }
59 | }
60 |
61 | - (void)loadData {
62 | // Must implement this method in subclass.
63 | }
64 |
65 | - (void)didReceiveMemoryWarning
66 | {
67 | [super didReceiveMemoryWarning];
68 | // Dispose of any resources that can be recreated.
69 | }
70 |
71 | #pragma mark - LoadData
72 |
73 | - (void)requestDataSuccess:(NSDictionary *)dataObject {
74 | [self.tableView reloadData];
75 | [self hideProgressView];
76 |
77 | [self finishRefresh];
78 | _loadingStatus = 0;
79 | }
80 |
81 | - (void)requestDataFailure:(NSString *)errorMessage {
82 | [self finishRefresh];
83 | _loadingStatus = 0;
84 | }
85 |
86 | - (BOOL)canStartNewLoading {
87 | return _loadingStatus == 0;
88 | }
89 |
90 | - (void)finishRefresh {
91 | // Refresh Control
92 | [self.refreshControl endRefreshing];
93 | self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"加载完成"];
94 | [self performSelector:@selector(afterFinishRefresh) withObject:nil afterDelay:0.5f];
95 | }
96 |
97 | - (void)afterFinishRefresh {
98 | self.refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:@"下拉刷新"];
99 | }
100 |
101 | #pragma mark - Table view data source
102 |
103 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
104 | {
105 | return 1;
106 | }
107 |
108 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
109 | {
110 | // Return the number of rows in the section.
111 | return [self.data count];
112 | }
113 |
114 | #pragma mark - Push to new controller method
115 | - (void)pushToSingleTopicViewController:(NSData *)dataObject {
116 | // Check if need to log in
117 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:dataObject];
118 | NSArray *messageArray = [doc searchWithXPathQuery:@"//div[@id='Wrapper']/div[@class='content']/div[@class='box']/div[@class='message']"];
119 | if ([messageArray count] > 0) {
120 | if ([[[messageArray objectAtIndex:0] text] isEqualToString:@"查看本主题需要登录"]) {
121 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:@"查看本主题需要登录" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
122 | [alert show];
123 | // If not login, show alert
124 | // TODO: still request to login if has logged in
125 | return;
126 | }
127 | }
128 |
129 | V2EXSingleTopicViewController *singleTopicController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"singleTopicController"];
130 | [singleTopicController loadNewTopicWithData:dataObject];
131 | [self.navigationController pushViewController:singleTopicController animated:YES];
132 | }
133 |
134 | #pragma marks -- UIAlertViewDelegate --
135 | -(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
136 | {
137 | if (buttonIndex == 1) {
138 | [self pushToUserLoginController];
139 | }
140 | }
141 |
142 | //- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
143 | //{
144 | // static NSString *CellIdentifier = @"Cell";
145 | // UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
146 | //
147 | // // Configure the cell...
148 | //
149 | // return cell;
150 | //}
151 |
152 | /*
153 | // Override to support conditional editing of the table view.
154 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
155 | {
156 | // Return NO if you do not want the specified item to be editable.
157 | return YES;
158 | }
159 | */
160 |
161 | /*
162 | // Override to support editing the table view.
163 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
164 | {
165 | if (editingStyle == UITableViewCellEditingStyleDelete) {
166 | // Delete the row from the data source
167 | [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
168 | }
169 | else if (editingStyle == UITableViewCellEditingStyleInsert) {
170 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
171 | }
172 | }
173 | */
174 |
175 | /*
176 | // Override to support rearranging the table view.
177 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
178 | {
179 | }
180 | */
181 |
182 | /*
183 | // Override to support conditional rearranging of the table view.
184 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
185 | {
186 | // Return NO if you do not want the item to be re-orderable.
187 | return YES;
188 | }
189 | */
190 |
191 | /*
192 | #pragma mark - Navigation
193 |
194 | // In a story board-based application, you will often want to do a little preparation before navigation
195 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
196 | {
197 | // Get the new view controller using [segue destinationViewController].
198 | // Pass the selected object to the new view controller.
199 | }
200 |
201 | */
202 |
203 | @end
204 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXTopicsListInSingleNodeViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTopicsListInSingleNodeViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/16/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "V2EXTableViewController.h"
12 | #import "V2EXSingleTopicViewController.h"
13 | #import "V2EXAfterTopicActionDelegate.h"
14 |
15 | @interface V2EXTopicsListInSingleNodeViewController : V2EXTableViewController
16 | {
17 | BOOL _isLogin;
18 | }
19 |
20 | @property (weak, nonatomic) NSMutableString *uri;
21 |
22 | //+ (V2EXTopicsListInSingleNodeViewController *)sharedController;
23 | - (void)loadNewNodeWithData:(NSData *)data;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXTopicsListInSingleNodeViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTopicsListInSingleNodeViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/16/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "TFHpple+V2EXMethod.h"
10 | #import "V2EXTopicsListInSingleNodeViewController.h"
11 | #import "V2EXTopicsListCell.h"
12 | #import "V2EXNormalModel.h"
13 | #import "V2EXMBProgressHUDUtil.h"
14 | #import "V2EXStringUtil.h"
15 | #import "V2EXNewTopicViewController.h"
16 |
17 | @interface V2EXTopicsListInSingleNodeViewController ()
18 |
19 | @end
20 |
21 | @implementation V2EXTopicsListInSingleNodeViewController
22 |
23 | //+ (V2EXTopicsListInSingleNodeViewController *)sharedController
24 | //{
25 | // static V2EXTopicsListInSingleNodeViewController *_sharedTopicsListInSingleNodeViewControllerInstance = nil;
26 | // static dispatch_once_t predicate;
27 | // dispatch_once(&predicate, ^{
28 | // _sharedTopicsListInSingleNodeViewControllerInstance = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"topicListInSingleNodeController"];
29 | // });
30 | //
31 | // return _sharedTopicsListInSingleNodeViewControllerInstance;
32 | //}
33 |
34 |
35 | - (void)viewDidLoad
36 | {
37 | [super viewDidLoad];
38 | self.tableView.rowHeight = 70; // TODO: Why don't storyboard with identifiertopicListInSingleNodeController support rowHeight?
39 | _loadingStatus = 1;
40 |
41 | // self.singleTopicViewController = [[UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]] instantiateViewControllerWithIdentifier:@"singleTopicController"];
42 | }
43 |
44 |
45 | - (void)didReceiveMemoryWarning
46 | {
47 | [super didReceiveMemoryWarning];
48 | // Dispose of any resources that can be recreated.
49 | }
50 |
51 | //- (void)viewWillAppear:(BOOL)animated {
52 | //}
53 |
54 |
55 | - (void)loadNewNodeWithData:(NSData *)data {
56 | // Reload data
57 | _loadingStatus = 1;
58 | [self requestDataSuccess:data];
59 |
60 | // Scroll to the top
61 | // [self.tableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
62 | }
63 |
64 | - (void)loadData {
65 | if ([self canStartNewLoading]) {
66 | _loadingStatus = 1;
67 | [self.model getTopicsList:self.uri];
68 | }
69 | }
70 |
71 | - (void)loadTopic:(NSUInteger)ID {
72 | if ([self canStartNewLoading]) {
73 | _loadingStatus = 2;
74 | [self showProgressView];
75 |
76 | [self.model getTopicWithID:ID];
77 | }
78 | }
79 |
80 | - (void)requestDataSuccess:(id)dataObject {
81 | if (_loadingStatus == 1) {
82 | [self handleListData:dataObject];
83 | } else {
84 | [self pushToSingleTopicViewController:dataObject];
85 | }
86 | [super requestDataSuccess:dataObject];
87 |
88 | }
89 |
90 |
91 | - (void)requestDataFailure:(NSString *)errorMessage {
92 | [super requestDataFailure:errorMessage];
93 | }
94 |
95 | - (void) handleListData:(id)dataObject {
96 | self.data = [[NSMutableArray alloc] init];
97 |
98 | TFHpple *doc = [[TFHpple alloc]initWithHTMLData:dataObject];
99 |
100 | //Check login
101 | _isLogin = [doc checkLogin];
102 |
103 | // Data Rows
104 | NSArray *elements = [doc searchWithXPathQuery:@"//body/div[2]/div/div/div[@class='cell']/table[1]"];
105 |
106 | for (TFHppleElement *element in elements) {
107 | TFHppleElement *avatarElement = [element searchFirstElementWithXPathQuery:@"//td[1]/a/img"];
108 | TFHppleElement *titleElement = [element searchFirstElementWithXPathQuery:@"//td[3]/span[@class='item_title']/a"];
109 | TFHppleElement *userNameElement = [element searchFirstElementWithXPathQuery:@"//td[3]/span[@class='small fade']/strong"];
110 | NSArray *replyElements = [element searchWithXPathQuery:@"//td[4]/a"];
111 |
112 | // Handle reply count
113 | NSString *replyCount;
114 | if ([replyElements count] > 0)
115 | {
116 | TFHppleElement *replyElement = [replyElements objectAtIndex:0];
117 | replyCount = [replyElement text];
118 | } else {
119 | replyCount = @"0";
120 | }
121 |
122 | NSString *link = [[element searchFirstElementWithXPathQuery:@"//td[3]/span[@class='item_title']/a"] objectForKey:@"href"];
123 |
124 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
125 | [V2EXStringUtil hanldeAvatarURL:[avatarElement objectForKey:@"src"]], @"avatar",
126 | [titleElement text], @"title",
127 | [userNameElement text], @"username",
128 | replyCount, @"replies",
129 | link, @"link", nil
130 | ];
131 | [self.data addObject:dict];
132 | }
133 | }
134 |
135 | #pragma mark - Table view data source
136 |
137 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
138 | {
139 | NSUInteger index = [indexPath row];
140 | static NSString *CellIdentifier = @"topicsListCell";
141 |
142 | UINib *nib = [UINib nibWithNibName:@"V2EXTopicsListCell" bundle:nil];
143 | [tableView registerNib:nib forCellReuseIdentifier:CellIdentifier];
144 |
145 | V2EXTopicsListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
146 |
147 | id rowData = [self.data objectAtIndex:index];
148 |
149 | cell.title.text = [rowData valueForKey:@"title"];
150 | cell.nodeTitle.text = @"";
151 | cell.replies.text = [rowData valueForKey:@"replies"];
152 | cell.username.text = [rowData valueForKey:@"username"];
153 | [cell.userAvatar setImageWithURL:[NSURL URLWithString:[rowData valueForKey:@"avatar"]] placeholderImage:[UIImage imageNamed:@"avatar_large"]];
154 |
155 | return cell;
156 | }
157 |
158 | - (NSUInteger)link2TopicID:(NSString *) urlString{
159 | NSError *error;
160 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"/t/[0-9]+#reply"
161 | options:0
162 | error:&error];
163 | if (regex != nil) {
164 | NSArray *array = [regex matchesInString: urlString
165 | options: 0
166 | range: NSMakeRange( 0, [urlString length])];
167 | if ([array count] > 0) {
168 | NSTextCheckingResult *match = [array objectAtIndex:0];
169 | NSRange firstHalfRange = [match rangeAtIndex:0];
170 | NSString *result = [[[urlString substringWithRange:firstHalfRange] stringByReplacingOccurrencesOfString:@"/t/" withString:@""] stringByReplacingOccurrencesOfString:@"#reply" withString:@""];
171 | return (NSUInteger)[result integerValue];
172 | } else {
173 | return 0;
174 | }
175 | } else {
176 | return 0;
177 | }
178 | }
179 |
180 | #pragma mark - Table view delegate
181 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
182 | NSUInteger topicID = [self link2TopicID:[[self.data objectAtIndex:[indexPath row]] objectForKey:@"link"]];
183 | _topicIDWillBePushedTo = topicID;
184 | [self loadTopic:topicID];
185 | }
186 |
187 | #pragma mark - Segue
188 | - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
189 | if ([identifier isEqualToString:@"toNewTopicController"]) {
190 | if (_isLogin) {
191 | return YES;
192 | }
193 | }
194 | [self showMessage:@"无法回复,可能因为您尚未登录"];
195 | return NO;
196 | }
197 |
198 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
199 | if ([[segue identifier] isEqualToString:@"toNewTopicController"]) {
200 | V2EXNewTopicViewController *newTopicController = [segue destinationViewController];
201 | newTopicController.uri = self.uri;
202 | newTopicController.lastController = self;
203 | }
204 | }
205 |
206 | #pragma mark - After Post New Topic
207 | - (void)afterCreateTopic:(NSData *)data {
208 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:data];
209 | NSUInteger topicID = (NSUInteger)[[[[doc searchFirstElementWithXPathQuery:@"//form"] objectForKey:@"action"] stringByReplacingOccurrencesOfString:@"/t/" withString:@""] integerValue];
210 | if (topicID > 0) {
211 | [self showMessage:@"新主题已创建"];
212 | [self pushToSingleTopicViewController:data];
213 | } else {
214 | [self showMessage:@"主题发布失败"];
215 | }
216 | }
217 |
218 | @end
219 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXUserLoginViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXUserLoginViewController.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/1/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "V2EXApiClient.h"
11 |
12 | @interface V2EXUserLoginViewController : UIViewController
13 | {
14 | //AFNetWorking
15 | AFHTTPSessionManager *_manager;
16 | AFHTTPRequestSerializer *_requestSerializer;
17 | AFHTTPResponseSerializer *_responseSerializer;
18 | }
19 |
20 | @property BOOL isLoadFromSeflController;
21 | @property (strong, nonatomic) IBOutlet UITextField *usernameTextField;
22 | @property (strong, nonatomic) IBOutlet UITextField *passwordTextField;
23 | @property (strong, nonatomic) IBOutlet UIButton *loginButton;
24 |
25 | - (IBAction)doLogin:(id)sender;
26 | - (IBAction)resetPassword:(id)sender;
27 |
28 | + (void)logout;
29 | @end
30 |
--------------------------------------------------------------------------------
/V2EX/Controllers/V2EXUserLoginViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXUserLoginViewController.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/1/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXUserLoginViewController.h"
10 | #import
11 | #import "UIViewController+MBProgressHUD.h"
12 | #import "TFHpple+V2EXMethod.h"
13 | //#import "V2EXGlobalCache.h"
14 |
15 | @interface V2EXUserLoginViewController ()
16 |
17 | @end
18 |
19 | @implementation V2EXUserLoginViewController
20 |
21 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
22 | {
23 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
24 | if (self) {
25 | // Custom initialization
26 | }
27 | return self;
28 | }
29 |
30 | - (void)viewDidLoad
31 | {
32 | [super viewDidLoad];
33 |
34 | // Set request and response serializer
35 | _manager = [[AFHTTPSessionManager alloc] init];
36 | _requestSerializer = [AFHTTPRequestSerializer serializer];
37 | _responseSerializer = [AFHTTPResponseSerializer serializer];
38 | [_requestSerializer setValue:@"http://v2ex.com/signin" forHTTPHeaderField:@"Referer"];
39 | [_requestSerializer setValue:DEFAULT_USET_AGENT forHTTPHeaderField:@"User-Agent"];
40 | [_responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:@"text/html", nil]];
41 | _manager.requestSerializer = _requestSerializer;
42 | _manager.responseSerializer = _responseSerializer;
43 |
44 | [self loadSavedUserData];
45 |
46 | if (_isLoadFromSeflController) {
47 | UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"IconMenu"] style:UIBarButtonItemStylePlain target:self action:@selector(showMenu:)];
48 | // btn.image = [UIImage imageNamed:@"IconMenu"];
49 |
50 | self.navigationItem.leftBarButtonItem = btn;
51 | }
52 |
53 | //To hide the keyboard when empty area is touched
54 | UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
55 | initWithTarget:self action:@selector(handleBackgroundTap:)];
56 | tapRecognizer.cancelsTouchesInView = NO;
57 | [self.view addGestureRecognizer:tapRecognizer];
58 | }
59 |
60 | - (void) handleBackgroundTap:(UITapGestureRecognizer*)sender
61 | {
62 | [self.usernameTextField resignFirstResponder];
63 | [self.passwordTextField resignFirstResponder];
64 | }
65 |
66 | - (BOOL)textFieldShouldReturn:(UITextField *)textField{
67 | [textField resignFirstResponder];
68 | return YES;
69 | }
70 |
71 | - (void)showMenu:(id)sender {
72 | [self.sideMenuViewController presentMenuViewController];
73 | }
74 |
75 | - (void)loadSavedUserData {
76 | NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
77 | NSString *username = [userDefaultes stringForKey:@"saved_username"];
78 | NSString *password = [userDefaultes stringForKey:@"saved_password"];
79 |
80 | if (username) {
81 | self.usernameTextField.text = username;
82 | if (password) {
83 | self.passwordTextField.text = password;
84 | [self doLogin:nil];
85 | }
86 | }
87 | }
88 |
89 | - (IBAction)doLogin:(id)sender {
90 |
91 | // [_model userLoginWithUsername:self.usernameTextField.text andPassword:self.passwordTextField.text];
92 | // [self.navigationController popViewControllerAnimated:YES];
93 | if ([self.usernameTextField.text length] > 0 && [self.passwordTextField.text length] > 0) {
94 | [self prepareForLogin];
95 | } else {
96 | [self showMessage:@"请输入用户名和密码"];
97 | }
98 |
99 | }
100 |
101 | - (IBAction)resetPassword:(id)sender {
102 | //TODO: support https
103 | [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://v2ex.com/forgot"]];
104 | }
105 |
106 | #pragma mark - Login selector
107 | - (void)prepareForLogin {
108 | [self showProgressView];
109 | self.usernameTextField.enabled = NO;
110 | self.passwordTextField.enabled = NO;
111 | self.loginButton.enabled = NO;
112 |
113 | [_manager GET:@"http://v2ex.com/signin" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
114 | TFHpple *doc = [[TFHpple alloc]initWithHTMLData:responseObject];
115 |
116 | NSString *next = [[doc searchFirstElementWithXPathQuery:@"//input[@name='next']"] objectForKey:@"value"];
117 | NSString *once = [[doc searchFirstElementWithXPathQuery:@"//input[@name='once']"] objectForKey:@"value"];
118 | NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:next, @"next", once, @"once", nil];
119 |
120 |
121 | [self performSelector:@selector(login:) withObject:dict afterDelay:1];
122 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
123 | [self showLoginNetworkError:error];
124 | }];
125 |
126 | }
127 |
128 | - (void)login:(NSDictionary *)dict {
129 | [_manager POST:@"http://v2ex.com/signin" parameters:[NSDictionary dictionaryWithObjectsAndKeys:self.usernameTextField.text, @"u", self.passwordTextField.text, @"p",
130 | [dict objectForKey:@"next"], @"next", [dict objectForKey:@"once"], @"once", nil] success:^(NSURLSessionDataTask *task, id responseObject) {
131 | // NSHTTPURLResponse *response = (NSHTTPURLResponse *)[task response];
132 | // NSDictionary *headers = [response allHeaderFields];
133 |
134 | // NSLog(@"%@", headers);
135 | // TODO: Handle 302 redirect
136 | if (YES/*[headers objectForKey:@"Location"]*/) {
137 | [_manager GET:@"http://v2ex.com/signin" parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
138 | // [task response
139 | [self validateLogin:responseObject];
140 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
141 | [self showLoginNetworkError:error];
142 | }];
143 | } else {
144 | [self validateLogin:responseObject];
145 | }
146 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
147 | [self showLoginNetworkError:error];
148 | }];
149 | }
150 |
151 | - (void)validateLogin:(NSData *)responseObject {
152 | TFHpple *doc = [[TFHpple alloc] initWithHTMLData:responseObject];
153 | if ([self checkLogin:doc]) {
154 | [self afterLogin];
155 | [self.navigationController popViewControllerAnimated:YES];
156 | [self showMessage:@"登录成功"];
157 | } else {
158 | // NSLog(@"data:%@", [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]);
159 | [self showLoginPasswordError];
160 | }
161 | }
162 |
163 | - (BOOL)checkLogin:(TFHpple *)doc {
164 | NSArray *usernameArray = [doc searchWithXPathQuery:@"//div[@id='Top']//table//td[@width='auto']//a[@class='top']"];
165 | if ([usernameArray count] > 0) {
166 | NSString *username =[[[usernameArray objectAtIndex:0] objectForKey:@"href"] stringByReplacingOccurrencesOfString:@"/member/" withString:@""];
167 | if ([[username lowercaseString] isEqualToString:[self.usernameTextField.text lowercaseString]]) {
168 | NSLog(@"User log in: %@", username);
169 | return YES;
170 | }
171 | }
172 | return NO;
173 | }
174 |
175 | /**
176 | * After Login set timestamp to "/user/lastlogin" in Global Cache
177 | *
178 | * @param username current username
179 | */
180 | - (void)afterLogin {
181 | [self saveUsernameAndPassword];
182 | [self finishLoginRequest];
183 | }
184 |
185 | /**
186 | * Logout - Just clean the cookies
187 | */
188 | + (void)logout {
189 | NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
190 | for (NSHTTPCookie *cookie in [storage cookies]) {
191 | [storage deleteCookie:cookie];
192 | }
193 | }
194 |
195 | - (void)finishLoginRequest {
196 | [self hideProgressView];
197 | self.usernameTextField.enabled = YES;
198 | self.passwordTextField.enabled = YES;
199 | self.loginButton.enabled = YES;
200 | }
201 |
202 | // Handle error
203 | - (void)showLoginNetworkError:(NSError *)error {
204 | [self finishLoginRequest];
205 | [self showMessage:@"登录过程中出现网络错误,请重试"];
206 |
207 | NSLog(@"[ERROR]Network Error during login action: %@", [error description]);
208 | }
209 |
210 | - (void)showLoginOtherError:(NSError *)error {
211 | [self finishLoginRequest];
212 | [self showMessage:@"无法检测您是否已经登录,请重试"];
213 | [V2EXUserLoginViewController logout];
214 | NSLog(@"[ERROR]Other Error(might about HTML DOM) during login action: %@", [error description]);
215 | }
216 |
217 | - (void)showLoginPasswordError {
218 | [self finishLoginRequest];
219 | [self showMessage:@"登录失败,用户名或密码错误"];
220 | [V2EXUserLoginViewController logout];
221 | }
222 |
223 | // Save username and password
224 |
225 | - (void)saveUsernameAndPassword {
226 | NSUserDefaults *userDefaultes = [NSUserDefaults standardUserDefaults];
227 | [userDefaultes setObject:self.usernameTextField.text forKey:@"saved_username"];
228 | [userDefaultes setObject:self.passwordTextField.text forKey:@"saved_password"];
229 | [userDefaultes synchronize];
230 | }
231 |
232 |
233 | @end
234 |
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-Small.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-Small@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "40x40",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-40@2x.png",
19 | "scale" : "2x"
20 | },
21 | {
22 | "size" : "57x57",
23 | "idiom" : "iphone",
24 | "filename" : "Icon.png",
25 | "scale" : "1x"
26 | },
27 | {
28 | "size" : "57x57",
29 | "idiom" : "iphone",
30 | "filename" : "Icon@2x.png",
31 | "scale" : "2x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-60@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "29x29",
41 | "idiom" : "ipad",
42 | "filename" : "Icon-Small-1.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "Icon-Small@2x-1.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "40x40",
53 | "idiom" : "ipad",
54 | "filename" : "Icon-40.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-40@2x-1.png",
61 | "scale" : "2x"
62 | },
63 | {
64 | "size" : "50x50",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-Small-50.png",
67 | "scale" : "1x"
68 | },
69 | {
70 | "size" : "50x50",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-Small-50@2x.png",
73 | "scale" : "2x"
74 | },
75 | {
76 | "size" : "72x72",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-72.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "size" : "72x72",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-72@2x.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "76x76",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-76.png",
91 | "scale" : "1x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-76@2x.png",
97 | "scale" : "2x"
98 | }
99 | ],
100 | "info" : {
101 | "version" : 1,
102 | "author" : "xcode"
103 | }
104 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40@2x-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-40@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-72.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-76.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/AppIcon.appiconset/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/AppIcon.appiconset/Icon@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconBack.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconBack@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconBack.imageset/IconBack@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconBack.imageset/IconBack@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconChatting.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "chatting.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconChatting.imageset/chatting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconChatting.imageset/chatting.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconEmpty.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconEmpty@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconEmpty.imageset/IconEmpty@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconEmpty.imageset/IconEmpty@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconHome.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconHome@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconHome.imageset/IconHome@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconHome.imageset/IconHome@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconKey.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconKey@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconKey.imageset/IconKey@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconKey.imageset/IconKey@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconMenu.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "icon_menu.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x",
11 | "filename" : "icon_menu@2x.png"
12 | }
13 | ],
14 | "info" : {
15 | "version" : 1,
16 | "author" : "xcode"
17 | }
18 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconMenu.imageset/icon_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconMenu.imageset/icon_menu.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconMenu.imageset/icon_menu@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconMenu.imageset/icon_menu@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconProfile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconProfile@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconProfile.imageset/IconProfile@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconProfile.imageset/IconProfile@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconRecent.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconRecent@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconRecent.imageset/IconRecent@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconRecent.imageset/IconRecent@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconRecent.imageset/timer-vector.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconRecent.imageset/timer-vector.psd
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconReply.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "speech-bubble-vector.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconReply.imageset/speech-bubble-vector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconReply.imageset/speech-bubble-vector.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconSettings.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "IconSettings@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/IconSettings.imageset/IconSettings@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/IconSettings.imageset/IconSettings@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "filename" : "Default@640x960.png",
9 | "scale" : "2x"
10 | },
11 | {
12 | "extent" : "full-screen",
13 | "idiom" : "iphone",
14 | "subtype" : "retina4",
15 | "filename" : "Default@640x1136-1.png",
16 | "minimum-system-version" : "7.0",
17 | "orientation" : "portrait",
18 | "scale" : "2x"
19 | },
20 | {
21 | "orientation" : "portrait",
22 | "idiom" : "ipad",
23 | "extent" : "full-screen",
24 | "minimum-system-version" : "7.0",
25 | "filename" : "Default@768x1024.png",
26 | "scale" : "1x"
27 | },
28 | {
29 | "orientation" : "portrait",
30 | "idiom" : "ipad",
31 | "extent" : "full-screen",
32 | "minimum-system-version" : "7.0",
33 | "filename" : "Default@1536x2048.png",
34 | "scale" : "2x"
35 | },
36 | {
37 | "orientation" : "portrait",
38 | "idiom" : "ipad",
39 | "extent" : "full-screen",
40 | "filename" : "Default@768x1024-1.png",
41 | "scale" : "1x"
42 | },
43 | {
44 | "orientation" : "portrait",
45 | "idiom" : "ipad",
46 | "extent" : "full-screen",
47 | "filename" : "Default@1536x2048-1.png",
48 | "scale" : "2x"
49 | }
50 | ],
51 | "info" : {
52 | "version" : 1,
53 | "author" : "xcode"
54 | }
55 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@1536x2048-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@1536x2048-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@1536x2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@1536x2048.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@640x1136-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@640x1136-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@640x960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@640x960.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@768x1024-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@768x1024-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage.launchimage/Default@768x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage.launchimage/Default@768x1024.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "filename" : "Default@640x960-1.png",
9 | "scale" : "2x"
10 | },
11 | {
12 | "extent" : "full-screen",
13 | "idiom" : "iphone",
14 | "subtype" : "retina4",
15 | "filename" : "Default@640x1136-1.png",
16 | "minimum-system-version" : "7.0",
17 | "orientation" : "portrait",
18 | "scale" : "2x"
19 | },
20 | {
21 | "orientation" : "portrait",
22 | "idiom" : "ipad",
23 | "extent" : "full-screen",
24 | "minimum-system-version" : "7.0",
25 | "filename" : "Default@768x1024-1.png",
26 | "scale" : "1x"
27 | },
28 | {
29 | "orientation" : "landscape",
30 | "idiom" : "ipad",
31 | "extent" : "full-screen",
32 | "minimum-system-version" : "7.0",
33 | "filename" : "Default@1024x768-1.png",
34 | "scale" : "1x"
35 | },
36 | {
37 | "orientation" : "portrait",
38 | "idiom" : "ipad",
39 | "extent" : "full-screen",
40 | "minimum-system-version" : "7.0",
41 | "filename" : "Default@1536x2048-1.png",
42 | "scale" : "2x"
43 | },
44 | {
45 | "orientation" : "landscape",
46 | "idiom" : "ipad",
47 | "extent" : "full-screen",
48 | "minimum-system-version" : "7.0",
49 | "filename" : "Default@2048x1536-1.png",
50 | "scale" : "2x"
51 | },
52 | {
53 | "orientation" : "portrait",
54 | "idiom" : "iphone",
55 | "extent" : "full-screen",
56 | "filename" : "Default@320x480.png",
57 | "scale" : "1x"
58 | },
59 | {
60 | "orientation" : "portrait",
61 | "idiom" : "iphone",
62 | "extent" : "full-screen",
63 | "filename" : "Default@640x960.png",
64 | "scale" : "2x"
65 | },
66 | {
67 | "orientation" : "portrait",
68 | "idiom" : "iphone",
69 | "extent" : "full-screen",
70 | "filename" : "Default@640x1136.png",
71 | "subtype" : "retina4",
72 | "scale" : "2x"
73 | },
74 | {
75 | "orientation" : "portrait",
76 | "idiom" : "ipad",
77 | "extent" : "to-status-bar",
78 | "filename" : "Default@768x1004.png",
79 | "scale" : "1x"
80 | },
81 | {
82 | "orientation" : "portrait",
83 | "idiom" : "ipad",
84 | "extent" : "full-screen",
85 | "filename" : "Default@768x1024.png",
86 | "scale" : "1x"
87 | },
88 | {
89 | "orientation" : "landscape",
90 | "idiom" : "ipad",
91 | "extent" : "to-status-bar",
92 | "filename" : "Default@1024x748.png",
93 | "scale" : "1x"
94 | },
95 | {
96 | "orientation" : "landscape",
97 | "idiom" : "ipad",
98 | "extent" : "full-screen",
99 | "filename" : "Default@1024x768.png",
100 | "scale" : "1x"
101 | },
102 | {
103 | "orientation" : "portrait",
104 | "idiom" : "ipad",
105 | "extent" : "to-status-bar",
106 | "filename" : "Default@1536x2008.png",
107 | "scale" : "2x"
108 | },
109 | {
110 | "orientation" : "portrait",
111 | "idiom" : "ipad",
112 | "extent" : "full-screen",
113 | "filename" : "Default@1536x2048.png",
114 | "scale" : "2x"
115 | },
116 | {
117 | "orientation" : "landscape",
118 | "idiom" : "ipad",
119 | "extent" : "to-status-bar",
120 | "filename" : "Default@2048x1496.png",
121 | "scale" : "2x"
122 | },
123 | {
124 | "orientation" : "landscape",
125 | "idiom" : "ipad",
126 | "extent" : "full-screen",
127 | "filename" : "Default@2048x1536.png",
128 | "scale" : "2x"
129 | }
130 | ],
131 | "info" : {
132 | "version" : 1,
133 | "author" : "xcode"
134 | }
135 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x748.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x748.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x768-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x768-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x768.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1024x768.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2008.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2008.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2048-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2048-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2048.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@1536x2048.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1496.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1496.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1536-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1536-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1536.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@2048x1536.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@320x480.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@320x480.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x1136-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x1136-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x1136.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x1136.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x960-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x960-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x960.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@640x960.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1004.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1004.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1024-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1024-1.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/LaunchImage2.launchimage/Default@768x1024.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/Stars.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x",
10 | "filename" : "Stars@2x.png"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/Stars.imageset/Stars@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/Stars.imageset/Stars@2x.png
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/avatar_large.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x",
6 | "filename" : "avatar_large.png"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | }
12 | ],
13 | "info" : {
14 | "version" : 1,
15 | "author" : "xcode"
16 | }
17 | }
--------------------------------------------------------------------------------
/V2EX/Images.xcassets/avatar_large.imageset/avatar_large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imWildCat/V2EX-iOS-ObjC/ce2022e087f8df8c99d102f001955bc286fd182e/V2EX/Images.xcassets/avatar_large.imageset/avatar_large.png
--------------------------------------------------------------------------------
/V2EX/Models/Data/NodesList/NodesList.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | qna
7 | share
8 | create
9 | autistic
10 | in
11 | random
12 | ideas
13 | design
14 | blog
15 | paper
16 |
17 |
18 | v2ex
19 | babel
20 | gae
21 | dns
22 | feedback
23 | picky
24 | guide
25 | olivida
26 |
27 |
28 | idev
29 | imarketing
30 | iad
31 | icode
32 | itransfer
33 |
34 |
35 | programmer
36 | python
37 | android
38 | linux
39 | cloud
40 | bitcoin
41 | php
42 | server
43 | hardware
44 | linode
45 | designer
46 | outsourcing
47 | kindle
48 | mysql
49 | bb
50 | tornado
51 | ror
52 | typography
53 | java
54 | markdown
55 | programming
56 | mongodb
57 | redis
58 | ruby
59 | car
60 | business
61 | photoshop
62 | lego
63 | math
64 | sony
65 |
66 |
67 | games
68 | igame
69 | bf3
70 | lol
71 | sc2
72 | steam
73 | ps3
74 | wow
75 | eve
76 | xbox360
77 | gt
78 | wii
79 |
80 |
81 | macosx
82 | iphone
83 | mbp
84 | ipad
85 | mba
86 | accessory
87 | imac
88 | macmini
89 | ipod
90 | macbook
91 | mobileme
92 | macpro
93 | iwork
94 | ilife
95 | garageband
96 |
97 |
98 | all4all
99 | jobs
100 | afterdark
101 | music
102 | movie
103 | exchange
104 | reading
105 | photograph
106 | tv
107 | taste
108 | creditcard
109 | invest
110 | travel
111 | lohas
112 | baby
113 | soccer
114 | pet
115 | bike
116 | tuan
117 | coffee
118 | plant
119 | diary
120 | love
121 | mushroom
122 | mileage
123 |
124 |
125 | google
126 | twitter
127 | facebook
128 | wikipedia
129 | path
130 | reddit
131 | 4sq
132 |
133 |
134 | beijing
135 | shanghai
136 | shenzhen
137 | hangzhou
138 | guangzhou
139 | chengdu
140 | kunming
141 | wuhan
142 | nyc
143 | tianjin
144 | la
145 | sanfrancisco
146 |
147 |
148 | uniqlo
149 | lamy
150 | ikea
151 | muji
152 | gap
153 | moleskine
154 | gstar
155 | nike
156 | adidas
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/V2EX/Models/Data/repliesList.css:
--------------------------------------------------------------------------------
1 | .dock_area {
2 | background-color: #EDF3F5;
3 | background-repeat: repeat-x;
4 | padding: 0;
5 | }
6 | .fade {
7 | color: #CCCCCC;
8 | }
9 | .fr {
10 | text-align: right;
11 | }
12 | .gray {
13 | color: #999999;
14 | }
15 | .chevron {
16 | font-family: "Lucida Grande";
17 | font-weight: 500;
18 | }
19 | a:link, a:visited, a:active {
20 | font-size:14px;
21 | color: #778087;
22 | text-decoration: none;
23 | }
24 | a {
25 | font-size:14px;
26 | color: #778087;
27 | text-decoration: none;
28 | }
29 | div {
30 | font-size:15px;
31 | }
--------------------------------------------------------------------------------
/V2EX/Models/V2EXAbstractModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAbstractModel.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/3/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "V2EXApiClient.h"
11 | #import "V2EXRequestDataDelegate.h"
12 |
13 | @interface V2EXAbstractModel : NSObject
14 | {
15 | id _delegate;
16 | };
17 |
18 |
19 | @property (nonatomic, assign) V2EXApiClient *apiClient;
20 |
21 | - (id) initWithDelegate:(id)delegate;
22 | - (NSDictionary *) parseData:(id)dataObject;
23 | - (void)loadDataSuccess:(id)dataObject;
24 | - (void)loadDataFailure:(NSError *)error;
25 | // Need to support request start and terminate request. For example, - (void)startLoadData & - (void)terminateLoading;
26 | // If not,
27 |
28 |
29 | //- (void)loadData:(NSString *)uri isGetMethod:(BOOL)isGetMethod isJsonApi:(BOOL)isJsonApi parameters:(NSDictionary *)parameters;
30 | // Base Data
31 | - (void)getJSONData:(NSString *)uri parameters:(NSDictionary *)parameter;
32 |
33 | // HTML Data
34 | - (void)getHTMLData:(NSString *)uri parameters:(NSDictionary *)parameter;
35 |
36 | - (void)postData:(NSString *)uri parameters:(NSDictionary *)parameter;
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXAbstractModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAbstractModel.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/3/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXAbstractModel.h"
10 | #import "V2EXMBProgressHUDUtil.h"
11 |
12 | @implementation V2EXAbstractModel
13 |
14 | - (id)init {
15 | [NSException raise:@"V2EXAbstractModel Exception" format:@"can't use init method"];
16 | return self;
17 | }
18 |
19 | - (id)initWithDelegate:(id )delegate {
20 | self = [super init];
21 | if(self){
22 | _delegate = delegate;
23 | self.apiClient = [V2EXApiClient sharedClient];
24 | }
25 | return self;
26 | }
27 |
28 | - (void)loadData:(NSString *)uri isGetMethod:(BOOL)isGetMethod isJsonApi:(BOOL)isJsonApi parameters:(NSDictionary *)parameters {
29 | [self.apiClient managerRequestData:uri isGetMethod:isGetMethod isJsonApi:isJsonApi parameters:parameters success:^(id dataObject) {
30 | [self loadDataSuccess:[self parseData:dataObject]];
31 | } failure:^(NSError *error) {
32 | [self loadDataFailure:error];
33 | }];
34 |
35 | }
36 |
37 | - (NSDictionary *) parseData:(id)dataObject {
38 | //Optional to be overwritten.
39 | return dataObject;
40 | }
41 |
42 | - (void)loadDataSuccess:(id)dataObject {
43 | [V2EXMBProgressHUDUtil dismissGlobalHUD];
44 | [_delegate requestDataSuccess:dataObject];
45 | }
46 |
47 | - (void)loadDataFailure:(NSError *)error {
48 | NSString *errorMessage;
49 | switch ([error code]) {
50 | case 444:
51 | errorMessage = @"抱歉,请等待上一次请求完成";
52 | break;
53 | case -1009:
54 | errorMessage = @"加载失败,似乎没有连接到网络";
55 | break;
56 | default:
57 | errorMessage = @"网络错误,加载失败";
58 | break;
59 | }
60 | [V2EXMBProgressHUDUtil dismissGlobalHUD];
61 |
62 | [V2EXMBProgressHUDUtil showMessage:errorMessage];
63 | if ([_delegate respondsToSelector:@selector(requestDataFailure:)]) {
64 | [_delegate requestDataFailure:errorMessage];
65 | }
66 |
67 | NSLog(@"[ERROR] Network: %@", [error description]);
68 | }
69 |
70 |
71 | // Base Data
72 | - (void)getJSONData:(NSString *)uri parameters:(NSDictionary *)parameter {
73 | [self loadData:uri isGetMethod:YES isJsonApi:YES parameters:parameter];
74 | }
75 |
76 | // HTML Data
77 | - (void)getHTMLData:(NSString *)uri parameters:(NSDictionary *)parameter {
78 | [self loadData:uri isGetMethod:YES isJsonApi:NO parameters:parameter];
79 | }
80 |
81 | - (void)postData:(NSString *)uri parameters:(NSDictionary *)parameter {
82 | // NSMutableString *uriWithParam = [uri mutableCopy];
83 | // if ([parameter count] > 0) {
84 | // [uriWithParam appendString:@"?"];
85 | // BOOL isFirst = YES;
86 | //
87 | // for (NSString *key in [parameter allKeys] ) {
88 | // NSString *value = [parameter objectForKey:key];
89 | //
90 | // if (!isFirst) {
91 | // [uriWithParam appendString:@"&"];
92 | // }
93 | //
94 | // [uriWithParam appendString:[NSString stringWithFormat:@"%@=%@", key, value]];
95 | // }
96 | // }
97 |
98 | // [self loadData:uriWithParam isGetMethod:NO isJsonApi:NO parameters:nil];
99 | [self loadData:uri isGetMethod:NO isJsonApi:NO parameters:parameter];
100 | }
101 |
102 | @end
103 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXNodesListModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNodesListModel.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/14/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import "V2EXRequestDataDelegate.h"
12 |
13 | @interface V2EXNodesListModel : NSObject
14 | {
15 | NSArray *_nodeURIs;
16 | NSMutableArray *_nodeTitles;
17 | NSMutableArray *_nodeHeaders;
18 | }
19 |
20 | @property(nonatomic, assign) id delegate;
21 |
22 | - (id)initWithIndex:(NSUInteger)index;
23 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
24 | @end
25 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXNodesListModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNodesListModel.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/14/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXNodesListModel.h"
10 | #import "V2EXNodesListCell.h"
11 |
12 | @implementation V2EXNodesListModel
13 |
14 | - (id)initWithIndex:(NSUInteger)idx {
15 |
16 | if ([super init]) {
17 | NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"NodesList" ofType:@"plist"];
18 | NSArray *array = [[NSArray alloc] initWithContentsOfFile:plistPath];
19 | _nodeURIs = [array objectAtIndex:idx];
20 |
21 | _nodeTitles = [[NSMutableArray alloc] init];
22 | _nodeHeaders = [[NSMutableArray alloc] init];
23 |
24 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
25 | NSString *documentDirectory = [paths objectAtIndex:0];
26 | NSString *dbPath = [documentDirectory stringByAppendingPathComponent:@"db/v2ex_normal.db"];
27 | FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
28 |
29 | [db open];
30 | for (NSString *nodeURI in _nodeURIs) {
31 | FMResultSet *retSet = [db executeQuery:@"SELECT * FROM nodes WHERE uri = ?",nodeURI];
32 | [retSet next];
33 | NSString *title = [retSet stringForColumn:@"title"];
34 | NSString *header = [retSet stringForColumn:@"header"];
35 | [_nodeTitles addObject:title];
36 | if (header) {
37 | [_nodeHeaders addObject:header];
38 | } else {
39 | [_nodeHeaders addObject:@""];
40 | }
41 | }
42 | [db close];
43 | }
44 | return self;
45 | }
46 |
47 | #pragma mark - Table view data source
48 |
49 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
50 | {
51 | return 1;
52 | }
53 |
54 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
55 | {
56 | return [_nodeTitles count];
57 | }
58 |
59 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
60 | {
61 | static NSString *CellIdentifier = @"nodesListCell";
62 | // UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
63 | // if (cell == nil) {
64 | // cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
65 | // } else {
66 | // while ([cell.contentView.subviews lastObject] != nil) {
67 | // [(UIView*)[cell.contentView.subviews lastObject]removeFromSuperview];
68 | // }
69 | // }
70 |
71 | UINib *nib = [UINib nibWithNibName:@"V2EXNodesListCell" bundle:nil];
72 | [tableView registerNib:nib forCellReuseIdentifier:CellIdentifier];
73 | V2EXNodesListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
74 | cell.delegate = self.delegate;
75 |
76 | NSUInteger index = [indexPath row];
77 | NSString *uri = [_nodeURIs objectAtIndex:index];
78 |
79 | cell.nodeTitle.text = [_nodeTitles objectAtIndex:index];
80 | cell.nodeHeader.text = [_nodeHeaders objectAtIndex:index];
81 | cell.nodeURI = uri;
82 |
83 | return cell;
84 | }
85 |
86 | #pragma mark - UITableViewDelegate
87 | // TODO: Find the better scheme for page view because this method don't working while UITableView in UISrollView
88 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
89 | NSUInteger index = indexPath.row;
90 | NSLog(@"%i",index);
91 | }
92 |
93 | @end
94 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXNormalModel.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNormalModel.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/8/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXAbstractModel.h"
10 |
11 | @interface V2EXNormalModel : V2EXAbstractModel
12 |
13 | // API
14 | - (void)getIndex;
15 | - (void)getTopicsList:(NSString *)URI;
16 | //- (void)getTopicWithLinkURI:(NSString *)URI;
17 |
18 |
19 | // User
20 | - (void)getUserInfo:(NSString *)username;
21 | - (void)getUserTopics:(NSString *)username;
22 | - (void)getUserReplies:(NSString *)username;
23 |
24 | // JSON API
25 | - (void)getAllNodes;
26 | - (void)getLatestTopics;
27 |
28 | // Topic
29 | - (void)getTopicWithID:(NSUInteger)ID;
30 | - (void)replyTopic:(NSUInteger)topicID andOnce:(NSUInteger)onceCode andContent:(NSString *)content;
31 | - (void)getNewTopicPage:(NSString *)uri;
32 | - (void)newTopic:(NSString *)uri andTitle:(NSString *)title andContent:(NSString *)content andOnce:(NSUInteger)once;
33 | - (void)reportTopic:(NSUInteger) topicID;
34 |
35 | @end
36 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXNormalModel.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNormalModel.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/8/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXNormalModel.h"
10 | #import "JSONAPIURI.h"
11 |
12 | @implementation V2EXNormalModel
13 |
14 | - (void)getIndex {
15 | [self getHTMLData:@"" parameters:nil];
16 | }
17 |
18 | - (void)getTopicsList:(NSString *)URI {
19 | [self getHTMLData:[@"go/" stringByAppendingString:URI] parameters:nil];
20 | }
21 |
22 | //- (void)getTopicWithLinkURI:(NSString *)URI {
23 | // [self getHTMLData:[URI stringByReplacingOccurrencesOfString:@"/t/" withString:@"t/"] parameters:nil];
24 | //}
25 |
26 |
27 | // User
28 | - (void)getUserInfo:(NSString *)username {
29 | [self getHTMLData:[NSString stringWithFormat:@"member/%@", username] parameters:nil];
30 | }
31 |
32 | - (void)getUserTopics:(NSString *)username {
33 | [self getHTMLData:[NSString stringWithFormat:@"member/%@/topics", username] parameters:nil];
34 | }
35 |
36 | - (void)getUserReplies:(NSString *)username {
37 | [self getHTMLData:[NSString stringWithFormat:@"member/%@/replies", username] parameters:nil];
38 | }
39 |
40 |
41 | // JSON API
42 | - (void)getLatestTopics {
43 | [self getJSONData:TOPICS_LATEST parameters:nil];
44 | }
45 |
46 | - (void)getAllNodes {
47 | [self getJSONData:NODES_ALL parameters:nil];
48 | }
49 |
50 | // Topic
51 | - (void)getTopicWithID:(NSUInteger)ID {
52 | [self getHTMLData:[NSString stringWithFormat:@"t/%i", (unsigned int)ID] parameters:nil];
53 | }
54 |
55 | - (void)replyTopic:(NSUInteger)topicID andOnce:(NSUInteger)onceCode andContent:(NSString *)content {
56 | [self postData:[NSString stringWithFormat:@"t/%i", topicID] parameters:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%i", onceCode], @"once", content, @"content", nil]];
57 | }
58 |
59 | - (void)getNewTopicPage:(NSString *)uri {
60 | [self getHTMLData:[NSString stringWithFormat:@"new/%@", uri] parameters:nil];
61 | }
62 |
63 | - (void)newTopic:(NSString *)uri andTitle:(NSString *)title andContent:(NSString *)content andOnce:(NSUInteger)once {
64 | [self postData:[NSString stringWithFormat:@"new/%@", uri] parameters:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"%i", once], @"once", title, @"title", content, @"content", nil]];
65 | }
66 |
67 | - (void)reportTopic:(NSUInteger)topicID {
68 | NSUInteger time = [[NSDate date] timeIntervalSince1970];
69 | [self getHTMLData:[NSString stringWithFormat:@"report/topic/%i?t=%i", topicID, time] parameters:nil];
70 | }
71 |
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/V2EX/Models/V2EXRequestDataDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXRequestDataDelegate.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/3/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol V2EXRequestDataDelegate
12 |
13 | @required
14 |
15 | - (void)requestDataSuccess:(id)dataObject;
16 |
17 | @optional
18 | - (void)requestDataFailure:(NSString *)errorMessage;
19 | - (void)requestTopicsList:(NSString *)nodeTitle andURI:(NSString *)URI;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/V2EX/ThirdParties/DRPaginatedScrollView/DRPaginatedScrollView.h:
--------------------------------------------------------------------------------
1 | //
2 | // DRPaginatedScrollView.h
3 | // Proday
4 | //
5 | // Created by David Román Aguirre on 24/12/13.
6 | // Copyright (c) 2013 David Román Aguirre. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface DRPaginatedScrollView : UIScrollView
12 |
13 | @property (nonatomic) NSTimeInterval jumpDurationPerPage;
14 | @property (readonly, nonatomic, getter = isJumping) BOOL jumping;
15 |
16 | @property (copy, nonatomic) void (^actionWhenTappedBlock)(DRPaginatedScrollView *);
17 |
18 | - (NSInteger)currentPage;
19 | - (NSInteger)nextPage;
20 | - (NSInteger)lastPage;
21 | - (NSInteger)numberOfPages;
22 |
23 | - (void)addPageWithHandler:(void (^)(UIView * pageView))handler;
24 | - (void)jumpToPage:(NSInteger)page bounce:(CGFloat)bounce completion:(void (^)(void))completion;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/V2EX/ThirdParties/DRPaginatedScrollView/DRPaginatedScrollView.m:
--------------------------------------------------------------------------------
1 | //
2 | // DRPaginatedScrollView.m
3 | // Proday
4 | //
5 | // Created by David Román Aguirre on 24/12/13.
6 | // Copyright (c) 2013 David Román Aguirre. All rights reserved.
7 | //
8 |
9 | #import "DRPaginatedScrollView.h"
10 |
11 | @interface DRPaginatedScrollView () {
12 | NSInteger previousPage;
13 | NSMutableArray * pageViews;
14 | }
15 |
16 | @property (strong, nonatomic) UITapGestureRecognizer * tapGestureRecognizer;
17 |
18 | @property (readwrite, nonatomic, getter = isJumping) BOOL jumping;
19 |
20 | @end
21 |
22 | @implementation DRPaginatedScrollView
23 |
24 | - (id)init {
25 | if (self = [super init]) {
26 | pageViews = [NSMutableArray new];
27 |
28 | [self setPagingEnabled:YES];
29 | [self setShowsHorizontalScrollIndicator:NO];
30 | [self setShowsVerticalScrollIndicator:NO];
31 | [self setJumpDurationPerPage:0.1];
32 |
33 | // [self setActionWhenTappedBlock:^(DRPaginatedScrollView * paginatedScrollView) {
34 | // [paginatedScrollView jumpToPage:[paginatedScrollView nextPage] bounce:0 completion:nil];
35 | // }];
36 |
37 | self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)];
38 | [self addGestureRecognizer:self.tapGestureRecognizer];
39 | }
40 |
41 | return self;
42 | }
43 |
44 | - (void)handleTap {
45 | if (self.actionWhenTappedBlock) self.actionWhenTappedBlock(self);
46 | }
47 |
48 | - (void)layoutSubviews {
49 | if (self.contentSize.width != self.frame.size.width*self.numberOfPages) {
50 | [self setContentSize:CGSizeMake(self.frame.size.width*self.numberOfPages, self.contentSize.height)];
51 | [self setContentOffset:CGPointMake(self.frame.size.width*previousPage, self.contentOffset.y)];
52 | } else {
53 | previousPage = [self currentPage];
54 | }
55 |
56 | [super layoutSubviews];
57 | }
58 |
59 | - (NSInteger)currentPage {
60 | return round(self.contentOffset.x/self.frame.size.width);
61 | }
62 |
63 | - (NSInteger)nextPage {
64 | return self.currentPage+1;
65 | }
66 |
67 | - (NSInteger)lastPage {
68 | return self.numberOfPages-1;
69 | }
70 |
71 | - (NSInteger)numberOfPages {
72 | return [pageViews count];
73 | }
74 |
75 | - (void)addPageWithHandler:(void (^)(UIView * pageView))handler {
76 | UIView * pageView = [UIView new];
77 |
78 | handler(pageView);
79 | [pageView setBackgroundColor:[UIColor clearColor]];
80 | [pageView setTranslatesAutoresizingMaskIntoConstraints:NO];
81 | [self addSubview:pageView];
82 |
83 | UIView * previousPageView;
84 |
85 | if (self.numberOfPages > 0) {
86 | previousPageView = pageViews[self.numberOfPages-1];
87 | }
88 |
89 | NSLayoutConstraint * topConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1 constant:0];
90 | NSLayoutConstraint * leftConstraint;
91 | NSLayoutConstraint * widthConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
92 | NSLayoutConstraint * heightConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
93 |
94 | if (previousPageView) {
95 | leftConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:previousPageView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
96 | } else {
97 | leftConstraint = [NSLayoutConstraint constraintWithItem:pageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
98 | }
99 |
100 | [self addConstraints:@[topConstraint, leftConstraint, widthConstraint, heightConstraint]];
101 |
102 | [pageViews addObject:pageView];
103 | }
104 |
105 | - (void)jumpToPage:(NSInteger)page bounce:(CGFloat)bounce completion:(void (^)(void))completion {
106 | if (!self.isJumping && page < [self numberOfPages]) {
107 | [self setJumping:YES];
108 |
109 | [self setPagingEnabled:NO];
110 | if (self.frame.size.width*page < self.contentOffset.x) bounce = -bounce;
111 |
112 | [UIView animateWithDuration:self.jumpDurationPerPage*[self numberOfPages] animations:^{
113 | [self setContentOffset:CGPointMake(self.frame.size.width*page+bounce, self.contentOffset.y)];
114 | } completion:^(BOOL finished) {
115 | [UIView animateWithDuration:self.jumpDurationPerPage*[self numberOfPages]/2.75 animations:^{
116 | [self setContentOffset:CGPointMake(self.contentOffset.x-bounce, self.contentOffset.y)];
117 | } completion:^(BOOL finished) {
118 | [self setJumping:NO];
119 | [self setPagingEnabled:YES];
120 | if (completion) completion();
121 | }];
122 | }];
123 | }
124 | }
125 |
126 | @end
127 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/JSONAPIURI.h:
--------------------------------------------------------------------------------
1 | //
2 | // JSONAPIURI.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/8/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #ifndef V2EX_JSONAPIURI_h
10 | #define V2EX_JSONAPIURI_h
11 |
12 | #define SITE_STATS @"site/stats.json"
13 | #define SITE_INFO @"site/info.json"
14 | #define NODES_ALL @"nodes/all.json"
15 | #define NODES_SHOW @"nodes/show.json"
16 | #define TOPICS_LATEST @"topics/latest.json"
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/NormalAPIURI.h:
--------------------------------------------------------------------------------
1 | //
2 | // NormalAPIURI.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/9/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #ifndef V2EX_NormalAPIURI_h
10 | #define V2EX_NormalAPIURI_h
11 |
12 |
13 |
14 | #endif
15 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/V2EXApiClient.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXApiClient.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/31/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #define DEFAULT_USET_AGENT @"5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16"
12 |
13 | @interface V2EXApiClient : NSObject
14 | {
15 | //Using AFHTTPSessionManager temporarily to support cookies
16 | AFHTTPSessionManager *_manager;
17 |
18 | BOOL _isHTTPS;
19 |
20 | BOOL _isLoading;
21 | }
22 | + (V2EXApiClient *)sharedClient;
23 |
24 | - (void) managerRequestData:(NSString *)uri isGetMethod:(BOOL)isGetMethod isJsonApi:(BOOL)isJsonApi parameters:(NSDictionary *)params success:(void (^)(id dataObject))success failure:(void (^)(NSError *error))failure;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/V2EXApiClient.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXApiClient.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/31/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXApiClient.h"
10 | #import "V2EXHttpRequestSerializer.h"
11 |
12 | #define HTTP_ROOT_URL @"http://www.v2ex.com/"
13 | #define HTTPS_ROOT_URL @"https://www.v2ex.com/"
14 | #define JSONAPI_URI @"api/"
15 | //TODO: Adding more support for user agent.
16 |
17 |
18 | @implementation V2EXApiClient
19 |
20 | - (id)init
21 | {
22 | self = [super init];
23 | _manager = [AFHTTPSessionManager manager];
24 | _isHTTPS = NO;
25 |
26 | return self;
27 | }
28 |
29 | + (V2EXApiClient *)sharedClient
30 | {
31 | static V2EXApiClient *_sharedClientInstance = nil;
32 | static dispatch_once_t predicate;
33 | dispatch_once(&predicate, ^{
34 | _sharedClientInstance = [[self alloc] init];
35 |
36 | });
37 |
38 | return _sharedClientInstance;
39 | }
40 |
41 | /**
42 | * Sets _manager.requestSerializer and _manager setResponseSerializer according to (BOOL)isJsonApi.
43 | *
44 | * @param isJsonApi If using V2EX JSON API.
45 | */
46 | - (void) _setSerializer:(BOOL)isJsonApi{
47 |
48 | if(isJsonApi){
49 | V2EXHttpRequestSerializer *requestSerializer = [V2EXHttpRequestSerializer serializer];
50 | [requestSerializer setValue:DEFAULT_USET_AGENT forHTTPHeaderField:@"User-Agent"];
51 | AFJSONResponseSerializer *responseSerializer = [AFJSONResponseSerializer serializer];
52 |
53 | [_manager setRequestSerializer:requestSerializer];
54 | [_manager setResponseSerializer:responseSerializer];
55 | } else {
56 | V2EXHttpRequestSerializer *requestSerializer = [V2EXHttpRequestSerializer serializer];
57 | [requestSerializer setValue:DEFAULT_USET_AGENT forHTTPHeaderField:@"User-Agent"];
58 | AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
59 | [responseSerializer setAcceptableContentTypes:[NSSet setWithObjects:@"text/html", nil]];
60 |
61 | [_manager setRequestSerializer:requestSerializer];
62 | [_manager setResponseSerializer:responseSerializer];
63 | }
64 | }
65 |
66 | /**
67 | * Returns V2EX base http or https url according to (BOOL)_isHTTPS.
68 | *
69 | * @return HTTP_ROOT_URL or HTTP_ROOT_URL.
70 | */
71 | - (NSString *) _getBaseUrl:(BOOL)isJsonApi{
72 | NSString *rootUrl;
73 | if(_isHTTPS){
74 | rootUrl = HTTPS_ROOT_URL;
75 | } else {
76 | rootUrl = HTTP_ROOT_URL;
77 | }
78 | if (isJsonApi) {
79 | return [rootUrl stringByAppendingString:JSONAPI_URI];
80 | } else {
81 | return rootUrl;
82 | }
83 | }
84 |
85 | - (void)managerRequestData:(NSString *)uri isGetMethod:(BOOL)isGetMethod isJsonApi:(BOOL)isJsonApi parameters:(NSDictionary *)params success:(void (^)(id dataObject))success failure:(void (^)(NSError *error))failure
86 | {
87 | [self _setSerializer:isJsonApi];
88 | NSString *url = [[self _getBaseUrl:isJsonApi] stringByAppendingString:uri];
89 | if (!_isLoading) {
90 | _isLoading = YES;
91 | if(isGetMethod){
92 | [_manager GET:url parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
93 | _isLoading = NO;
94 | success(responseObject);
95 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
96 | _isLoading = NO;
97 | failure(error);
98 | }];
99 | } else {
100 | [_manager POST:url parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
101 | _isLoading = NO;
102 | success(responseObject);
103 | } failure:^(NSURLSessionDataTask *task, NSError *error) {
104 | _isLoading = NO;
105 | failure(error);
106 | }];
107 | }
108 | } else {
109 | NSError *error = [[NSError alloc] initWithDomain:@"V2EX" code:444 userInfo:nil];
110 | failure(error);
111 | }
112 |
113 | }
114 |
115 |
116 | - (void) _managerGetJson:(NSString *)uri parameters:(NSDictionary *)params success:(void (^)(NSDictionary *jsonData))success failure:(void (^)(NSString *errorMessage))failure
117 | {
118 | [self managerRequestData:uri isGetMethod:YES isJsonApi:YES parameters:nil success:^(id data) {
119 | success(data);
120 | } failure:^(NSError *error) {
121 | failure([error description]);
122 | }];
123 | }
124 |
125 |
126 |
127 |
128 | @end
129 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/V2EXHttpRequestSerializer.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXHttpRequestSerializer.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/7/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "AFURLRequestSerialization.h"
10 |
11 | @interface V2EXHttpRequestSerializer : AFHTTPRequestSerializer
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/V2EX/Utilities/Network/V2EXHttpRequestSerializer.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXHttpRequestSerializer.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/7/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXHttpRequestSerializer.h"
10 |
11 | #define TIMEOUT 30
12 |
13 | @implementation V2EXHttpRequestSerializer
14 |
15 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing *)error
16 | {
17 | NSMutableURLRequest *request = [super requestWithMethod:method URLString:URLString parameters:parameters error:error];
18 | NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
19 | NSDictionary *headers = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
20 | [request setAllHTTPHeaderFields:headers];
21 | [request setTimeoutInterval:TIMEOUT];
22 | [request setCachePolicy:NSURLRequestReloadIgnoringLocalCacheData];
23 | return request;
24 | }
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/V2EX/Utilities/TFHpple+V2EXMethod.h:
--------------------------------------------------------------------------------
1 | //
2 | // TFHpple+V2EXMethod.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/6/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "TFHpple.h"
10 | #import "TFHppleElement+V2EXMethod.h"
11 |
12 | @interface TFHpple (V2EXMethod)
13 |
14 | - (TFHppleElement *)searchFirstElementWithXPathQuery:(NSString *)query;
15 | - (BOOL)checkLogin;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Utilities/TFHpple+V2EXMethod.m:
--------------------------------------------------------------------------------
1 | //
2 | // TFHpple+V2EXMethod.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/6/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "TFHpple+V2EXMethod.h"
10 |
11 | @implementation TFHpple (V2EXMethod)
12 |
13 | - (TFHppleElement *)searchFirstElementWithXPathQuery:(NSString *)query {
14 | NSArray *retArray = [self searchWithXPathQuery:query];
15 | if ([retArray count] > 0) {
16 | return [retArray objectAtIndex:0];
17 | }
18 | return nil;
19 | }
20 |
21 | - (BOOL)checkLogin {
22 | TFHppleElement *userLinkElement = [self searchFirstElementWithXPathQuery:@"//div[@id='Top']//table//td[@width='auto']//a[@class='top']"];
23 | if (userLinkElement) {
24 | NSString *username =[[userLinkElement objectForKey:@"href"] stringByReplacingOccurrencesOfString:@"/member/" withString:@""];
25 | if ([username length] > 0) {
26 | return YES;
27 | }
28 | }
29 | return NO;
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/V2EX/Utilities/TFHppleElement+V2EXMethod.h:
--------------------------------------------------------------------------------
1 | //
2 | // TFHppleElement+V2EXMethod.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/18/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "TFHppleElement.h"
10 |
11 | @interface TFHppleElement (V2EXMethod)
12 |
13 | - (TFHppleElement *)searchFirstElementWithXPathQuery:(NSString *)query;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/V2EX/Utilities/TFHppleElement+V2EXMethod.m:
--------------------------------------------------------------------------------
1 | //
2 | // TFHppleElement+V2EXMethod.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/18/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "TFHppleElement+V2EXMethod.h"
10 |
11 | @implementation TFHppleElement (V2EXMethod)
12 |
13 | // TODO: Is there any better implementation
14 | - (TFHppleElement *)searchFirstElementWithXPathQuery:(NSString *)query {
15 | NSArray *retArray = [self searchWithXPathQuery:query];
16 | if ([retArray count] > 0) {
17 | return [retArray objectAtIndex:0];
18 | }
19 | return nil;
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/UIView+FrameMethods.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+FrameMethods.h
3 | // V2EX
4 | //
5 | // The file created and edited by WildCat on 2/11/14.
6 | // Reference: http://segmentfault.com/q/1010000000119371
7 | //
8 |
9 | #import
10 |
11 | @interface UIView (FrameMethods)
12 | //Move methods
13 |
14 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical;
15 |
16 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded;
17 |
18 | - (void)moveToHorizontal:(CGFloat)horizontal;
19 |
20 | - (void)moveToVertical:(CGFloat)vertical;
21 |
22 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical;
23 |
24 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical setWidth:(CGFloat)width setHeight:(CGFloat)height;
25 |
26 | //Set width/height
27 |
28 | - (void)setWidth:(CGFloat)width height:(CGFloat)height;
29 |
30 | - (void)setWidth:(CGFloat)width;
31 |
32 | - (void)setHeight:(CGFloat)height;
33 |
34 | //Add width/height
35 |
36 | - (void)addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded;
37 |
38 | - (void)addWidth:(CGFloat)widthAdded;
39 |
40 | - (void)addHeight:(CGFloat)heightAdded;
41 |
42 | //Set corner radius
43 |
44 | - (void)setCornerRadius:(CGFloat)radius;
45 |
46 | - (void)setCornerRadius:(CGFloat)radius borderColor:(UIColor *)borderColor;
47 |
48 | - (CGRect)frameInWindow;
49 | @end
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/UIView+FrameMethods.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+FrameMethods.m
3 | // V2EX
4 | //
5 | // The file created and edited by WildCat on 2/11/14.
6 | // Reference: http://segmentfault.com/q/1010000000119371
7 | //
8 |
9 | #import "UIView+FrameMethods.h"
10 |
11 | @implementation UIView (FrameMethods)
12 |
13 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical
14 | {
15 | CGRect originRect = self.frame;
16 | CGRect newRect = CGRectMake(originRect.origin.x + horizontal, originRect.origin.y + vertical, originRect.size.width, originRect.size.height);
17 | self.frame = newRect;
18 | }
19 |
20 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded
21 | {
22 | CGRect originRect = self.frame;
23 | CGRect newRect = CGRectMake(originRect.origin.x + horizontal,
24 | originRect.origin.y + vertical,
25 | originRect.size.width + widthAdded,
26 | originRect.size.height + heightAdded);
27 | self.frame = newRect;
28 | }
29 |
30 | - (void)moveToHorizontal:(CGFloat)horizontal
31 | {
32 | CGRect originRect = self.frame;
33 | CGRect newRect = CGRectMake(horizontal, originRect.origin.y, originRect.size.width, originRect.size.height);
34 | self.frame = newRect;
35 | }
36 |
37 | - (void)moveToVertical:(CGFloat)vertical
38 | {
39 | CGRect originRect = self.frame;
40 | CGRect newRect = CGRectMake(originRect.origin.x, vertical, originRect.size.width, originRect.size.height);
41 | [self setFrame:newRect];
42 | }
43 |
44 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical
45 | {
46 | CGRect originRect = self.frame;
47 | CGRect newRect = CGRectMake(horizontal, vertical, originRect.size.width, originRect.size.height);
48 | self.frame = newRect;
49 | }
50 |
51 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical setWidth:(CGFloat)width setHeight:(CGFloat)height
52 | {
53 | CGRect newRect = CGRectMake(horizontal, vertical, width, height);
54 | self.frame = newRect;
55 | }
56 |
57 | - (void)setWidth:(CGFloat)width height:(CGFloat)height
58 | {
59 | CGRect originRect = self.frame;
60 | CGRect newRect = CGRectMake(originRect.origin.x, originRect.origin.y, width, height);
61 | self.frame = newRect;
62 | }
63 |
64 | - (void)setWidth:(CGFloat)width
65 | {
66 | CGRect originRect = self.frame;
67 | CGRect newRect = CGRectMake(originRect.origin.x, originRect.origin.y, width, originRect.size.height);
68 | self.frame = newRect;
69 | }
70 |
71 | - (void)setHeight:(CGFloat)height
72 | {
73 | NSLog(@"set height");
74 | CGRect originRect = self.frame;
75 | CGRect newRect = CGRectMake(originRect.origin.x, originRect.origin.y, originRect.size.width, height);
76 | self.frame = newRect;
77 | }
78 |
79 | - (void)addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded
80 | {
81 | CGRect originRect = self.frame;
82 | CGFloat newWidth = originRect.size.width + widthAdded;
83 | CGFloat newHeight = originRect.size.height + heightAdded;
84 | CGRect newRect = CGRectMake(originRect.origin.x, originRect.origin.y, newWidth, newHeight);
85 | self.frame = newRect;
86 | }
87 |
88 | - (void)addWidth:(CGFloat)widthAdded
89 | {
90 | [self addWidth:widthAdded addHeight:0];
91 | }
92 |
93 | - (void)addHeight:(CGFloat)heightAdded
94 | {
95 | [self addWidth:0 addHeight:heightAdded];
96 | }
97 |
98 | - (void)setCornerRadius:(CGFloat)radius
99 | {
100 | [self setCornerRadius:radius borderColor:[UIColor grayColor]];
101 | }
102 |
103 | - (void)setCornerRadius:(CGFloat)radius borderColor:(UIColor *)borderColor
104 | {
105 | [self.layer setBackgroundColor:[[UIColor whiteColor] CGColor]];
106 | [self.layer setBorderColor:[borderColor CGColor]];
107 | [self.layer setBorderWidth:1.0];
108 | [self.layer setCornerRadius:radius];
109 | [self.layer setMasksToBounds:YES];
110 | self.clipsToBounds = YES;
111 | }
112 |
113 | - (CGRect)frameInWindow
114 | {
115 | CGRect frameInWindow = [self.superview convertRect:self.frame toView:self.window];
116 | return frameInWindow;
117 | }
118 | @end
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/V2EXAppSize.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAppSize.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/9/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface V2EXAppSize : NSObject
12 |
13 | + (CGFloat)getHeightWithController:(UIViewController *)controller;
14 | + (CGFloat)getWidthWithController:(UIViewController *)controller;
15 | + (CGFloat)getStatusBarHeight;
16 | + (CGFloat)getNavBarHeight4Controller:(UIViewController *)controller;
17 |
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/V2EXAppSize.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAppSize.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/9/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXAppSize.h"
10 |
11 | @implementation V2EXAppSize
12 |
13 | + (CGFloat)_getSystemHeight {
14 | // CGRect rect = [[UIScreen mainScreen] bounds];
15 | CGRect rect = [[UIScreen mainScreen] applicationFrame];
16 | CGSize size = rect.size;
17 | return size.width;
18 | }
19 |
20 | + (CGFloat)_getSystemWidth {
21 | // CGRect rect = [[UIScreen mainScreen] bounds];
22 | CGRect rect = [[UIScreen mainScreen] applicationFrame];
23 | CGSize size = rect.size;
24 | return size.height;
25 | }
26 |
27 | + (CGFloat)getHeightWithController:(UIViewController *)controller {
28 | if (UIDeviceOrientationIsPortrait(controller.interfaceOrientation)) {
29 | return [self _getSystemWidth];
30 | } else {
31 | return [self _getSystemHeight];
32 | }
33 | }
34 |
35 | + (CGFloat)getWidthWithController:(UIViewController *)controller {
36 | if (UIDeviceOrientationIsPortrait(controller.interfaceOrientation)) {
37 | return [self _getSystemHeight];
38 | } else {
39 | return [self _getSystemWidth];
40 | }
41 | }
42 |
43 | + (CGFloat)getStatusBarHeight {
44 | CGRect rect = [[UIApplication sharedApplication] statusBarFrame];
45 | return rect.size.height;
46 | }
47 |
48 | + (CGFloat)getNavBarHeight4Controller:(UIViewController *)controller {
49 | return controller.navigationController.navigationBar.frame.size.height;
50 | }
51 |
52 | @end
53 |
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/V2EXMBProgressHUDUtil.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMBProgressHUDUtil.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | @interface V2EXMBProgressHUDUtil : NSObject
13 |
14 | + (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title;
15 | + (void)dismissGlobalHUD;
16 | + (MBProgressHUD*)showMessage:(NSString*)msg;
17 | @end
18 |
--------------------------------------------------------------------------------
/V2EX/Utilities/UI/V2EXMBProgressHUDUtil.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXMBProgressHUDUtil.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXMBProgressHUDUtil.h"
10 |
11 | #define ALPHA 0.70f
12 |
13 | @implementation V2EXMBProgressHUDUtil
14 |
15 | + (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
16 | UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
17 | [MBProgressHUD hideAllHUDsForView:window animated:YES];
18 |
19 | MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
20 | hud.labelText = title;
21 | hud.alpha = ALPHA;
22 | hud.userInteractionEnabled = NO;
23 | return hud;
24 | }
25 |
26 | + (void)dismissGlobalHUD {
27 | UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
28 | [MBProgressHUD hideHUDForView:window animated:YES];
29 | }
30 |
31 | + (MBProgressHUD*)showMessage:(NSString*)msg {
32 | UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
33 | // [MBProgressHUD hideAllHUDsForView:window animated:YES];
34 |
35 | static MBProgressHUD* hud = nil;
36 | // if (!hud) {
37 | hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
38 | // }
39 | hud.mode = MBProgressHUDModeText;
40 | hud.labelText = msg;
41 | hud.hidden = NO;
42 | hud.alpha = ALPHA;
43 | [hud hide:YES afterDelay:2.5f];
44 | hud.userInteractionEnabled = NO;
45 | return hud;
46 | }
47 |
48 | @end
49 |
--------------------------------------------------------------------------------
/V2EX/Utilities/V2EXGlobalCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXGlobalCache.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface V2EXGlobalCache : NSCache
12 |
13 | + (V2EXGlobalCache *)cache;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/V2EX/Utilities/V2EXGlobalCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXGlobalCache.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXGlobalCache.h"
10 | #import "V2EXAppDelegate.h"
11 |
12 | @implementation V2EXGlobalCache
13 |
14 | + (V2EXGlobalCache *)cache {
15 | static V2EXGlobalCache *_cache = nil;
16 | static dispatch_once_t predicate;
17 | dispatch_once(&predicate, ^{
18 | _cache = [[V2EXGlobalCache alloc] init];
19 | });
20 |
21 | return _cache;
22 | }
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/V2EX/Utilities/V2EXStringUtil.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXStringUtil.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/14/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface V2EXStringUtil : NSObject
12 |
13 | + (NSUInteger)link2TopicID:(NSString *)urlString;
14 | + (NSString *)replaceUnicode:(NSString *)unicodeStr;
15 | + (NSString *)stringByStrippingHTML:(NSString *)inputString;
16 | + (NSString *)filterHTML:(NSString *)html;
17 | + (NSString *)hanldeAvatarURL:(NSString *)url;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/V2EX/Utilities/V2EXStringUtil.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXStringUtil.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/14/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXStringUtil.h"
10 |
11 | @implementation V2EXStringUtil
12 |
13 | /**
14 | * Conver link to topic ID
15 | *
16 | * @param urlString The link in V2EX topics list.
17 | *
18 | * @return topic ID.
19 | */
20 | + (NSUInteger)link2TopicID:(NSString *)urlString{
21 | NSError *error;
22 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"/t/[0-9]+#reply"
23 | options:0
24 | error:&error];
25 | if (regex != nil) {
26 | NSArray *array = [regex matchesInString: urlString
27 | options: 0
28 | range: NSMakeRange( 0, [urlString length])];
29 | if ([array count] > 0) {
30 | NSTextCheckingResult *match = [array objectAtIndex:0];
31 | NSRange firstHalfRange = [match rangeAtIndex:0];
32 | NSString *result = [[[urlString substringWithRange:firstHalfRange] stringByReplacingOccurrencesOfString:@"/t/" withString:@""] stringByReplacingOccurrencesOfString:@"#reply" withString:@""];
33 | return (NSUInteger)[result integerValue];
34 | } else {
35 | return 0;
36 | }
37 | } else {
38 | return 0;
39 | }
40 | }
41 |
42 | + (NSString *)filterHTML:(NSString *)html
43 | {
44 | NSScanner * scanner = [NSScanner scannerWithString:html];
45 | NSString * text = nil;
46 | while([scanner isAtEnd] == NO)
47 | {
48 | [scanner scanUpToString:@"<" intoString:nil];
49 | [scanner scanUpToString:@">" intoString:&text];
50 | html = [html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%@>",text] withString:@""];
51 | }
52 | // NSString * regEx = @"<([^>]*)>";
53 | // html = [html stringByReplacingOccurrencesOfString:regEx withString:@""];
54 | return html;
55 | }
56 |
57 | + (NSString *)stringByStrippingHTML:(NSString *)inputString
58 | {
59 | NSMutableString *outString;
60 |
61 | if (inputString)
62 | {
63 | outString = [[NSMutableString alloc] initWithString:inputString];
64 |
65 | if ([inputString length] > 0)
66 | {
67 | NSRange r;
68 |
69 | while ((r = [outString rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
70 | {
71 | [outString deleteCharactersInRange:r];
72 | }
73 | }
74 | }
75 |
76 | return outString;
77 | }
78 |
79 | + (NSString *)replaceUnicode:(NSString *)unicodeStr {
80 |
81 | NSString *tempStr1 = [unicodeStr stringByReplacingOccurrencesOfString:@"\\u" withString:@"\\U"];
82 | NSString *tempStr2 = [tempStr1 stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
83 | NSString *tempStr3 = [[@"\"" stringByAppendingString:tempStr2] stringByAppendingString:@"\""];
84 | NSData *tempData = [tempStr3 dataUsingEncoding:NSUTF8StringEncoding];
85 | NSString* returnStr = [NSPropertyListSerialization propertyListFromData:tempData
86 | mutabilityOption:NSPropertyListImmutable
87 | format:NULL
88 | errorDescription:NULL];
89 |
90 | // NSLog(@"Output = %@", returnStr);
91 |
92 | return [returnStr stringByReplacingOccurrencesOfString:@"\\r\\n" withString:@"\n"];
93 | }
94 |
95 | /**
96 | * Handle V2EX avatar URL
97 | *
98 | * @param url avatar URL
99 | *
100 | * @return avatar url with protocol name http or https(not implementation yet)
101 | */
102 | + (NSString *)hanldeAvatarURL:(NSString *)url {
103 | return [@"http:" stringByAppendingString:url];
104 | //TODO: Support https.
105 | }
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/V2EX/V2EX-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIcons
12 |
13 | CFBundleIcons~ipad
14 |
15 | CFBundleIdentifier
16 | name.wildcat.v2ex
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | ${PRODUCT_NAME}
21 | CFBundlePackageType
22 | APPL
23 | CFBundleShortVersionString
24 | 0.1.2
25 | CFBundleSignature
26 | ????
27 | CFBundleVersion
28 | 2
29 | LSRequiresIPhoneOS
30 |
31 | UIMainStoryboardFile
32 | Main
33 | UIRequiredDeviceCapabilities
34 |
35 | armv7
36 |
37 | UIStatusBarHidden
38 |
39 | UISupportedInterfaceOrientations
40 |
41 | UIInterfaceOrientationPortrait
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/V2EX/V2EX-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header
3 | //
4 | // The contents of this file are implicitly included at the beginning of every source file.
5 | //
6 |
7 | #import
8 |
9 | #ifndef __IPHONE_5_0
10 | #warning "This project uses features only available in iOS SDK 5.0 and later."
11 | #endif
12 |
13 | #ifdef __OBJC__
14 | #import
15 | #import
16 | #endif
17 |
--------------------------------------------------------------------------------
/V2EX/V2EXAppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAppDelegate.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "V2EXLatestTopicsViewController.h"
12 | #import "V2EXNodesListViewController.h"
13 | #import "V2EXSelfViewController.h"
14 | #import
15 |
16 | @interface V2EXAppDelegate : UIResponder
17 |
18 | @property (strong, nonatomic) UIWindow *window;
19 |
20 | @property (retain, nonatomic) V2EXNodesListViewController *sharedNodesListViewController;
21 | @property (retain, nonatomic) V2EXLatestTopicsViewController *sharedLatestTopicsViewController;
22 | @property (retain, nonatomic) V2EXSelfViewController *sharedSelfViewController;
23 |
24 | @property (strong, nonatomic) V2EXGlobalCache *cache;
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/V2EX/V2EXAppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXAppDelegate.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXAppDelegate.h"
10 | #import
11 | #import
12 | #import "V2EXStringUtil.h"
13 |
14 | @implementation V2EXAppDelegate
15 |
16 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
17 | {
18 | self.cache = [V2EXGlobalCache cache];
19 | [self prepareForLaunching];
20 | return YES;
21 | }
22 |
23 | - (void)applicationWillResignActive:(UIApplication *)application
24 | {
25 | // 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.
26 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
27 | }
28 |
29 | - (void)applicationDidEnterBackground:(UIApplication *)application
30 | {
31 | // 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.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 | - (void)applicationWillEnterForeground:(UIApplication *)application
36 | {
37 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
38 | }
39 |
40 | - (void)applicationDidBecomeActive:(UIApplication *)application
41 | {
42 | // 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.
43 | }
44 |
45 | - (void)applicationWillTerminate:(UIApplication *)application
46 | {
47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
48 | }
49 |
50 | - (void)prepareForLaunching {
51 | [self prepareForNodeList];
52 | [self prepareForAllControllers];
53 | }
54 |
55 | - (void)prepareForNodeList {
56 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
57 | NSFileManager *fileMgr = [NSFileManager new];
58 |
59 | NSString *documentDirectory = [paths objectAtIndex:0];
60 |
61 | [fileMgr createDirectoryAtPath:[documentDirectory stringByAppendingString:@"/db"] withIntermediateDirectories:YES attributes:nil error:nil];
62 |
63 | NSString *dbPath = [documentDirectory stringByAppendingPathComponent:@"db/v2ex_normal.db"];
64 | FMDatabase *db = [FMDatabase databaseWithPath:dbPath] ;
65 | if (![db open]) {
66 | NSLog(@"Could not open db.");
67 | }
68 |
69 | int nodesCount = [db intForQuery:@"SELECT COUNT(*) FROM nodes"];
70 |
71 | if (nodesCount > 700) {
72 | NSLog(@"nodes > 700");
73 | } else {
74 | [db executeUpdate:@"CREATE TABLE IF NOT EXISTS nodes (id integer NOT NULL PRIMARY KEY UNIQUE,name text NOT NULL,title text NOT NULL,uri text NOT NULL,topics integer NOT NULL,header text,footer text,created text NOT NULL)"];
75 |
76 |
77 | NSBundle *bundle = [NSBundle mainBundle];
78 | NSString *plistPath = [bundle pathForResource:@"NodesAll" ofType:@"json"];
79 | NSData *jsonData = [NSData dataWithContentsOfFile:plistPath];
80 | id json = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
81 |
82 | for (NSDictionary *row in json) {
83 | NSString *header, *footer;
84 | if ([row objectForKey:@"header"] != [NSNull null]) {
85 | header = [V2EXStringUtil stringByStrippingHTML:[row objectForKey:@"header"]];
86 | } else {
87 | header = nil;
88 | }
89 | if ([row objectForKey:@"footer"] != [NSNull null]) {
90 | footer = [V2EXStringUtil stringByStrippingHTML:[row objectForKey:@"footer"]];
91 | } else {
92 | footer = nil;
93 | }
94 |
95 | [db executeUpdate:@"INSERT OR REPLACE INTO nodes (id, name, title, uri, topics, header, footer, created) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
96 | [row objectForKey:@"id"],
97 | [row objectForKey:@"name"],
98 | [row objectForKey:@"title"],
99 | [[row objectForKey:@"url"] stringByReplacingOccurrencesOfString:@"http://www.v2ex.com/go/" withString:@""],
100 | [row objectForKey:@"topics"],
101 | header,
102 | footer,
103 | [row objectForKey:@"created"]
104 | ];
105 | }
106 | }
107 | // V2EXJSONModel *nodesListModel = [[V2EXJSONModel alloc] initWithDelegate:self];
108 | // [nodesListModel getAllNodes];
109 | }
110 |
111 | - (void)prepareForAllControllers {
112 | _sharedLatestTopicsViewController = [V2EXLatestTopicsViewController sharedController];
113 | _sharedNodesListViewController = [V2EXNodesListViewController sharedController];
114 | _sharedSelfViewController = [V2EXSelfViewController sharedController];
115 | }
116 |
117 |
118 |
119 | //- (void)requestDataSuccess:(id)dataObject {
120 | // _dictAllNodes = dataObject;
121 | //}
122 |
123 | @end
124 |
--------------------------------------------------------------------------------
/V2EX/Views/Categories/UIView+FrameMethods.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+FrameMethods.h
3 | // V2EX
4 | //
5 | // The file created and edited by WildCat on 2/11/14.
6 | // Reference: http://segmentfault.com/q/1010000000119371
7 | //
8 |
9 | #import
10 |
11 | @interface UIView (FrameMethods)
12 | //Move methods
13 |
14 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical;
15 |
16 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded;
17 |
18 | - (void)moveToHorizontal:(CGFloat)horizontal;
19 |
20 | - (void)moveToVertical:(CGFloat)vertical;
21 |
22 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical;
23 |
24 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical setWidth:(CGFloat)width setHeight:(CGFloat)height;
25 |
26 | //Set width/height
27 |
28 | - (void)setWidth:(CGFloat)width height:(CGFloat)height;
29 |
30 | - (void)setWidth:(CGFloat)width;
31 |
32 | - (void)setHeight:(CGFloat)height;
33 |
34 | //Add width/height
35 |
36 | - (void)addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded;
37 |
38 | - (void)addWidth:(CGFloat)widthAdded;
39 |
40 | - (void)addHeight:(CGFloat)heightAdded;
41 |
42 | //Set corner radius
43 |
44 | - (void)setCornerRadius:(CGFloat)radius;
45 |
46 | - (void)setCornerRadius:(CGFloat)radius borderColor:(UIColor *)borderColor;
47 |
48 | - (CGRect)frameInWindow;
49 | @end
50 |
--------------------------------------------------------------------------------
/V2EX/Views/Categories/UIView+FrameMethods.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+FrameMethods.m
3 | // V2EX
4 | //
5 | // The file created and edited by WildCat on 2/11/14.
6 | // Reference: http://segmentfault.com/q/1010000000119371
7 | //
8 |
9 | #import "UIView+FrameMethods.h"
10 |
11 | @implementation UIView (FrameMethods)
12 |
13 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical
14 | {
15 | CGRect origionRect = self.frame;
16 | CGRect newRect = CGRectMake(origionRect.origin.x + horizontal, origionRect.origin.y + vertical, origionRect.size.width, origionRect.size.height);
17 | self.frame = newRect;
18 | }
19 |
20 | - (void)moveHorizontal:(CGFloat)horizontal vertical:(CGFloat)vertical addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded
21 | {
22 | CGRect origionRect = self.frame;
23 | CGRect newRect = CGRectMake(origionRect.origin.x + horizontal,
24 | origionRect.origin.y + vertical,
25 | origionRect.size.width + widthAdded,
26 | origionRect.size.height + heightAdded);
27 | self.frame = newRect;
28 | }
29 |
30 | - (void)moveToHorizontal:(CGFloat)horizontal
31 | {
32 | CGRect origionRect = self.frame;
33 | CGRect newRect = CGRectMake(horizontal, origionRect.origin.y, origionRect.size.width, origionRect.size.height);
34 | self.frame = newRect;
35 | }
36 |
37 | - (void)moveToVertical:(CGFloat)vertical
38 | {
39 | CGRect origionRect = self.frame;
40 | CGRect newRect = CGRectMake(origionRect.origin.x, vertical, origionRect.size.width, origionRect.size.height);
41 | [self setFrame:newRect];
42 | }
43 |
44 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical
45 | {
46 | CGRect origionRect = self.frame;
47 | CGRect newRect = CGRectMake(horizontal, vertical, origionRect.size.width, origionRect.size.height);
48 | self.frame = newRect;
49 | }
50 |
51 | - (void)moveToHorizontal:(CGFloat)horizontal toVertical:(CGFloat)vertical setWidth:(CGFloat)width setHeight:(CGFloat)height
52 | {
53 | CGRect newRect = CGRectMake(horizontal, vertical, width, height);
54 | self.frame = newRect;
55 | }
56 |
57 | - (void)setWidth:(CGFloat)width height:(CGFloat)height
58 | {
59 | CGRect origionRect = self.frame;
60 | CGRect newRect = CGRectMake(origionRect.origin.x, origionRect.origin.y, width, height);
61 | self.frame = newRect;
62 | }
63 |
64 | - (void)setWidth:(CGFloat)width
65 | {
66 | CGRect origionRect = self.frame;
67 | CGRect newRect = CGRectMake(origionRect.origin.x, origionRect.origin.y, width, origionRect.size.height);
68 | self.frame = newRect;
69 | }
70 |
71 | - (void)setHeight:(CGFloat)height
72 | {
73 | CGRect origionRect = self.frame;
74 | CGRect newRect = CGRectMake(origionRect.origin.x, origionRect.origin.y, origionRect.size.width, height);
75 | self.frame = newRect;
76 | }
77 |
78 | - (void)addWidth:(CGFloat)widthAdded addHeight:(CGFloat)heightAdded
79 | {
80 | CGRect originRect = self.frame;
81 | CGFloat newWidth = originRect.size.width + widthAdded;
82 | CGFloat newHeight = originRect.size.height + heightAdded;
83 | CGRect newRect = CGRectMake(originRect.origin.x, originRect.origin.y, newWidth, newHeight);
84 | self.frame = newRect;
85 | }
86 |
87 | - (void)addWidth:(CGFloat)widthAdded
88 | {
89 | [self addWidth:widthAdded addHeight:0];
90 | }
91 |
92 | - (void)addHeight:(CGFloat)heightAdded
93 | {
94 | [self addWidth:0 addHeight:heightAdded];
95 | }
96 |
97 | - (void)setCornerRadius:(CGFloat)radius
98 | {
99 | [self setCornerRadius:radius borderColor:[UIColor grayColor]];
100 | }
101 |
102 | - (void)setCornerRadius:(CGFloat)radius borderColor:(UIColor *)borderColor
103 | {
104 | [self.layer setBackgroundColor:[[UIColor whiteColor] CGColor]];
105 | [self.layer setBorderColor:[borderColor CGColor]];
106 | [self.layer setBorderWidth:1.0];
107 | [self.layer setCornerRadius:radius];
108 | [self.layer setMasksToBounds:YES];
109 | self.clipsToBounds = YES;
110 | }
111 |
112 | - (CGRect)frameInWindow
113 | {
114 | CGRect frameInWindow = [self.superview convertRect:self.frame toView:self.window];
115 | return frameInWindow;
116 | }
117 | @end
118 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXNodesListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNodesListCell.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/16/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "V2EXRequestDataDelegate.h"
11 |
12 | @interface V2EXNodesListCell : UITableViewCell
13 |
14 | @property (strong, nonatomic) IBOutlet UILabel *nodeTitle;
15 | @property (strong, nonatomic) IBOutlet UILabel *nodeHeader;
16 |
17 | @property (strong, nonatomic) NSString *nodeURI;
18 | @property(nonatomic, assign) id delegate;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXNodesListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXNodesListCell.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/16/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXNodesListCell.h"
10 |
11 | @implementation V2EXNodesListCell
12 |
13 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
14 | {
15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
16 | if (self) {
17 | // Initialization code
18 | }
19 | return self;
20 | }
21 |
22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated
23 | {
24 | [super setSelected:selected animated:animated];
25 | // Configure the view for the selected state
26 | }
27 |
28 | - (void)awakeFromNib {
29 | self.userInteractionEnabled = YES;
30 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cellClick)];
31 | [self addGestureRecognizer:tap];
32 | // Add responder for click event.
33 | }
34 |
35 | - (void)cellClick {
36 | // TODO: This is very bad implementation to respond click event in UITableViewCell as a result of using UITableView in UIScrollView. Need to improve. The page view(DRPaginatedScrollView) in V2EXNodesListController should be replaced by one doesn't inherit form UIScrollView.
37 | [self.delegate requestTopicsList:self.nodeTitle.text andURI:self.nodeURI];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXNodesListCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXTopicsListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTopicsListCell.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface V2EXTopicsListCell : UITableViewCell
12 | @property (weak, nonatomic) IBOutlet UILabel *title;
13 | @property (weak, nonatomic) IBOutlet UIImageView *userAvatar;
14 | @property (weak, nonatomic) IBOutlet UILabel *nodeTitle;
15 | @property (weak, nonatomic) IBOutlet UILabel *replies;
16 | @property (weak, nonatomic) IBOutlet UILabel *username;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXTopicsListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXTopicsListCell.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 2/2/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXTopicsListCell.h"
10 |
11 | @implementation V2EXTopicsListCell
12 |
13 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
14 | {
15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
16 | if (self) {
17 | // Initialization code
18 | [self setAutoresizingMask:UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth];
19 | }
20 | return self;
21 | }
22 |
23 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated
24 | {
25 | [super setSelected:selected animated:animated];
26 |
27 | // Configure the view for the selected state
28 | }
29 |
30 | - (id)initWithCoder:(NSCoder*)aDecoder
31 | {
32 | self = [super initWithCoder:aDecoder];
33 |
34 | if(self)
35 | {
36 | //Changes here after init'ing self
37 | }
38 |
39 | return self;
40 | }
41 |
42 | - (void)awakeFromNib
43 | {
44 | [self.replies.layer setCornerRadius:8];
45 | [self.userAvatar.layer setCornerRadius:self.userAvatar.frame.size.width/5];
46 | self.userAvatar.layer.masksToBounds = YES;
47 |
48 | self.layer.shouldRasterize = YES;
49 | self.layer.rasterizationScale = [[UIScreen mainScreen] scale];
50 | }
51 |
52 |
53 |
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXTopicsListCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
27 |
35 |
42 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXUserTopicsListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXUserTopicsListCell.h
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/23/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface V2EXUserTopicsListCell : UITableViewCell
12 |
13 | @property (weak, nonatomic) IBOutlet UILabel *title;
14 | @property (weak, nonatomic) IBOutlet UILabel *nodeTitle;
15 | @property (weak, nonatomic) IBOutlet UILabel *replies;
16 | @property (weak, nonatomic) IBOutlet UILabel *lastReplyUsername;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXUserTopicsListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // V2EXUserTopicsListCell.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 3/23/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import "V2EXUserTopicsListCell.h"
10 |
11 | @implementation V2EXUserTopicsListCell
12 |
13 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
14 | {
15 | self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
16 | if (self) {
17 | // Initialization code
18 | }
19 | return self;
20 | }
21 |
22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated
23 | {
24 | [super setSelected:selected animated:animated];
25 |
26 | // Configure the view for the selected state
27 | }
28 |
29 | @end
30 |
--------------------------------------------------------------------------------
/V2EX/Views/TableViewCells/V2EXUserTopicsListCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
23 |
31 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/V2EX/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/V2EX/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // V2EX
4 | //
5 | // Created by WildCat on 1/30/14.
6 | // Copyright (c) 2014 WildCat. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "V2EXAppDelegate.h"
12 |
13 | int main(int argc, char * argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([V2EXAppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------