() {
96 | @Override
97 | public void onSubscribe(Disposable d) {
98 |
99 | }
100 | @Override
101 | public void onNext(LCUser avUser) {
102 | // 注册成功,把用户对象赋值给当前用户 LCUser.getCurrentUser()
103 | startActivity(new Intent(RegisterActivity.this, MainActivity.class));
104 | RegisterActivity.this.finish();
105 | }
106 | @Override
107 | public void onError(Throwable e) {
108 | // 失败的原因可能有多种,常见的是用户名已经存在。
109 | showProgress(false);
110 | Toast.makeText(RegisterActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
111 | }
112 | @Override
113 | public void onComplete() {
114 |
115 | }
116 | });
117 | }
118 | }
119 |
120 | private boolean isusernameValid(String username) {
121 | //TODO: Replace this with your own logic
122 | return username.contains("@");
123 | }
124 |
125 | private boolean isPasswordValid(String password) {
126 | //TODO: Replace this with your own logic
127 | return password.length() > 4;
128 | }
129 |
130 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
131 | private void showProgress(final boolean show) {
132 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
133 | // for very easy animations. If available, use these APIs to fade-in
134 | // the progress spinner.
135 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
136 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
137 |
138 | mRegisterFormView.setVisibility(show ? View.GONE : View.VISIBLE);
139 | mRegisterFormView.animate().setDuration(shortAnimTime).alpha(
140 | show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
141 | @Override
142 | public void onAnimationEnd(Animator animation) {
143 | mRegisterFormView.setVisibility(show ? View.GONE : View.VISIBLE);
144 | }
145 | });
146 |
147 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
148 | mProgressView.animate().setDuration(shortAnimTime).alpha(
149 | show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
150 | @Override
151 | public void onAnimationEnd(Animator animation) {
152 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
153 | }
154 | });
155 | } else {
156 | // The ViewPropertyAnimator APIs are not available, so simply show
157 | // and hide the relevant UI components.
158 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
159 | mRegisterFormView.setVisibility(show ? View.GONE : View.VISIBLE);
160 | }
161 | }
162 |
163 | @Override
164 | public boolean onOptionsItemSelected(MenuItem item) {
165 | if (item.getItemId() == android.R.id.home) {
166 | onBackPressed();
167 | }
168 | return super.onOptionsItemSelected(item);
169 | }
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/Android/app/src/main/java/cn/leancloud/leanstoragegettingstarted/RoundedTransformation.java:
--------------------------------------------------------------------------------
1 | package cn.leancloud.leanstoragegettingstarted;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapShader;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.RectF;
8 | import android.graphics.Shader;
9 |
10 | import com.squareup.picasso.Transformation;
11 |
12 | /**
13 | * Created by BinaryHB on 16/9/18.
14 | */
15 | public class RoundedTransformation implements Transformation{
16 |
17 | private final int radius;
18 | private final int margin; // dp
19 |
20 | // radius is corner radii in dp
21 | // margin is the board in dp
22 | public RoundedTransformation(final int radius, final int margin) {
23 | this.radius = radius;
24 | this.margin = margin;
25 | }
26 |
27 | @Override
28 | public Bitmap transform(final Bitmap source) {
29 | final Paint paint = new Paint();
30 | paint.setAntiAlias(true);
31 | paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
32 |
33 | Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
34 | Canvas canvas = new Canvas(output);
35 | canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
36 |
37 | if (source != output) {
38 | source.recycle();
39 | }
40 |
41 | return output;
42 | }
43 |
44 | @Override
45 | public String key() {
46 | return "rounded(radius=" + radius + ", margin=" + margin + ")";
47 | }
48 | }
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/activity_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
22 |
23 |
32 |
33 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
46 |
47 |
50 |
51 |
62 |
63 |
64 |
65 |
73 |
74 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
20 |
21 |
27 |
28 |
29 |
30 |
34 |
35 |
36 |
37 |
38 |
39 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/activity_publish.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
16 |
17 |
21 |
22 |
28 |
29 |
36 |
37 |
44 |
45 |
49 |
50 |
60 |
61 |
69 |
70 |
71 |
72 |
80 |
81 |
82 |
83 |
84 |
85 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/activity_register.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
21 |
22 |
26 |
27 |
32 |
33 |
36 |
37 |
44 |
45 |
46 |
47 |
50 |
51 |
61 |
62 |
63 |
64 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
20 |
21 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/item_list_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
20 |
21 |
28 |
29 |
41 |
42 |
51 |
52 |
53 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Android/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | StorageStarted
3 | 注销
4 | 登录
5 | 发布
6 | 登录
7 | 注册
8 | 用户名
9 | 商品描述
10 | 从相册选择一张照片
11 | 提交
12 | 详情
13 | 密码
14 | 注册并登录
15 | 登录
16 | 邮件格式错误
17 | 密码大于 4 位
18 | 密码错误
19 | 这个是必填项
20 | 注册
21 |
22 |
--------------------------------------------------------------------------------
/Android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Android/app/src/test/java/cn/leancloud/leanstoragegettingstarted/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.leancloud.leanstoragegettingstarted;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/Android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.6.4'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | google()
19 | jcenter()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/Android/gradle.properties:
--------------------------------------------------------------------------------
1 | android.enableJetifier=true
2 | android.useAndroidX=true
--------------------------------------------------------------------------------
/Android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/Android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Oct 09 16:29:33 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
7 |
--------------------------------------------------------------------------------
/Android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/Android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # StorageStarted
2 |
3 | 此 demo 与官方文档配套进行使用。
4 |
5 | [查看 Android 文档](https://leancloud.cn/docs/leanstorage_guide-java.html)
6 |
7 | [查看 iOS 文档](https://leancloud.cn/docs/leanstorage_guide-objc.html)
8 |
9 | [查看 JavaScript 文档](https://leancloud.cn/docs/leanstorage_guide-js.html)
10 |
--------------------------------------------------------------------------------
/Web/README.MD:
--------------------------------------------------------------------------------
1 | # LeanCloud 存储 - JavaScript 入门 Demo
2 |
3 | 入口位于 login 文件夹下的 login.html
4 |
--------------------------------------------------------------------------------
/Web/header.css:
--------------------------------------------------------------------------------
1 | /* Everything but the jumbotron gets side spacing for mobile first views */
2 | .header,
3 | .marketing,
4 | .footer {
5 | padding-right: 15px;
6 | padding-left: 15px;
7 | }
8 |
9 | /* Custom page header */
10 | .header {
11 | padding-bottom: 20px;
12 | }
13 | /* Make the masthead heading the same height as the navigation */
14 | .header h3 {
15 | margin-top: 0;
16 | margin-bottom: 0;
17 | line-height: 40px;
18 | }
19 |
20 | /* Custom page footer */
21 | .footer {
22 | padding-top: 19px;
23 | color: #777;
24 | border-top: 1px solid #e5e5e5;
25 | }
26 |
27 | /* Responsive: Portrait tablets and up */
28 | @media screen and (min-width: 768px) {
29 | /* Remove the padding we set earlier */
30 | .header,
31 | .marketing,
32 | .footer {
33 | padding-right: 0;
34 | padding-left: 0;
35 | }
36 | /* Space out the masthead */
37 | .header {
38 | margin-bottom: 30px;
39 | }
40 | /* Remove the bottom border on the jumbotron for visual effect */
41 | .jumbotron {
42 | border-bottom: 0;
43 | }
44 | }
--------------------------------------------------------------------------------
/Web/initLeanCloud.js:
--------------------------------------------------------------------------------
1 | // LeanCloud - 初始化 - 将这里的 appId 、 appKey 和 serverURL 替换成自己的应用数据
2 | // https://leancloud.cn/docs/sdk_setup-js.html#hash14962003
3 | const appId = "OLoj899IwHYi787ClrImlr3k-gzGzoHsz";
4 | const appKey = "gkz35mRTqTE2aqwp7dEr5uEE";
5 | const serverURL = "https://OLoj899I.lc-cn-n1-shared.com";
6 |
7 | LC.init({ appId, appKey, serverURL });
8 |
--------------------------------------------------------------------------------
/Web/login/login.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | background-color: #eee;
5 | }
6 | .form-signin {
7 | max-width: 330px;
8 | padding: 15px;
9 | margin: 0 auto;
10 | }
11 | .form-signin .form-signin-heading,
12 | .form-signin .checkbox {
13 | margin-bottom: 10px;
14 | }
15 | .form-signin .checkbox {
16 | font-weight: normal;
17 | }
18 | .form-signin .form-control {
19 | position: relative;
20 | height: auto;
21 | -webkit-box-sizing: border-box;
22 | -moz-box-sizing: border-box;
23 | box-sizing: border-box;
24 | padding: 10px;
25 | font-size: 16px;
26 | }
27 | .form-signin .form-control:focus {
28 | z-index: 2;
29 | }
30 | .form-signin input[type="username"] {
31 | margin-bottom: -1px;
32 | border-bottom-right-radius: 0;
33 | border-bottom-left-radius: 0;
34 | }
35 | .form-signin input[type="password"] {
36 | margin-bottom: 10px;
37 | border-top-left-radius: 0;
38 | border-top-right-radius: 0;
39 | }
40 |
--------------------------------------------------------------------------------
/Web/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StorageStarted
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
36 |
37 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/Web/login/login.js:
--------------------------------------------------------------------------------
1 | function login() {
2 | const username = $("#inputUsername").val();
3 | const password = $("#inputPassword").val();
4 |
5 | // LeanCloud - 登录
6 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash964666
7 | LC.User.login(username, password)
8 | .then(() => {
9 | window.location.href = "./../products-list/products-list.html";
10 | })
11 | .catch(({ error }) => alert(error));
12 | }
13 |
14 | $(function () {
15 | $(".form-signin").on("submit", function (e) {
16 | e.preventDefault();
17 | login();
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/Web/new-product/new-product.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | background-color: #eee;
5 | }
6 | .new-product {
7 | max-width: 430px;
8 | padding: 15px;
9 | margin: 0 auto;
10 | }
11 | .new-product .new-product-heading,
12 | .new-product .checkbox {
13 | margin-bottom: 10px;
14 | }
15 | .new-product .checkbox {
16 | font-weight: normal;
17 | }
18 | .new-product .form-control {
19 | position: relative;
20 | height: auto;
21 | -webkit-box-sizing: border-box;
22 | -moz-box-sizing: border-box;
23 | box-sizing: border-box;
24 | padding: 10px;
25 | font-size: 16px;
26 | }
27 | .new-product .form-control:focus {
28 | z-index: 2;
29 | }
30 | .new-product input {
31 | margin-bottom: 10px;
32 | }
33 | .new-product textarea {
34 | margin-bottom: 10px;
35 | }
36 |
--------------------------------------------------------------------------------
/Web/new-product/new-product.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StorageStarted
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
28 |
29 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/Web/new-product/new-product.js:
--------------------------------------------------------------------------------
1 | function releaseNewProduct() {
2 | // LeanCloud - 当前用户
3 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash748191977
4 | const currentUser = LC.User.current();
5 |
6 | // LeanCloud - 文件
7 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash825935
8 | const file = $("#inputFile")[0].files[0];
9 | const name = file.name;
10 | LC.File.upload(name, file).then((lcFile) => {
11 | // LeanCloud - 对象
12 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash799084270
13 | LC.CLASS("Product")
14 | .add({
15 | title: $("#inputTitle").val(),
16 | price: parseFloat($("#inputPrice").val()),
17 | description: $("#inputDescription").val(),
18 | owner: currentUser,
19 | image: lcFile,
20 | })
21 | .then(() => {
22 | window.location.href = "./../products-list/products-list.html";
23 | })
24 | .catch(({ error }) => alert(error));
25 | });
26 | }
27 |
28 | $(function () {
29 | if (LC.User.current()) {
30 | $(".new-product").on("submit", function (e) {
31 | e.preventDefault();
32 | releaseNewProduct();
33 | });
34 | } else {
35 | window.location.href = "./../login/login.html";
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/Web/products-list/products-list.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | background-color: #eee;
5 | }
6 | .new-product {
7 | margin-bottom: 20px;
8 | }
9 |
--------------------------------------------------------------------------------
/Web/products-list/products-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StorageStarted
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
40 |
41 |
42 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Web/products-list/products-list.js:
--------------------------------------------------------------------------------
1 | // handlebars context
2 | const context = {
3 | products: [],
4 | };
5 |
6 | function getReleaseTime(date) {
7 | const year = date.getFullYear();
8 | const month = date.getMonth() + 1;
9 | const day = date.getDate();
10 | return `${year}/${month}/${day}`;
11 | }
12 |
13 | function setupData() {
14 | // LeanCloud - 查询
15 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash860317
16 | LC.CLASS("Product")
17 | .include("owner", "image")
18 | .orderBy("createdAt", "desc")
19 | .find()
20 | .then((products) => {
21 | products.forEach((product) => {
22 | const owner = product.data.owner;
23 | const productImage = product.data.image;
24 |
25 | // handlebars context
26 | context.products.push({
27 | productImageUrl: productImage ? productImage.url : "./../storage.png",
28 | productTitle: product.data.title,
29 | productDescription: product.data.description,
30 | price: product.data.price,
31 | ownerUsername: owner ? owner.data.username : "unknown",
32 | releaseTime: getReleaseTime(product.createdAt),
33 | });
34 | });
35 |
36 | // use handlebars to update html
37 | const source = $("#products-list").html();
38 | const template = Handlebars.compile(source);
39 | const html = template(context);
40 | $(".products-detail").html(html);
41 | })
42 | .catch((error) => alert(error.error));
43 | }
44 |
45 | function logout() {
46 | LC.User.logOut();
47 | window.location.href = "./../login/login.html";
48 | }
49 |
50 | $(function () {
51 | if (LC.User.current()) {
52 | setupData();
53 | } else {
54 | window.location.href = "./../login/login.html";
55 | }
56 | });
57 |
--------------------------------------------------------------------------------
/Web/signup/signup.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 20px;
3 | padding-bottom: 20px;
4 | background-color: #eee;
5 | }
6 | .form-signup {
7 | max-width: 330px;
8 | padding: 15px;
9 | margin: 0 auto;
10 | }
11 | .form-signup .form-signup-heading,
12 | .form-signup .checkbox {
13 | margin-bottom: 10px;
14 | }
15 | .form-signup .checkbox {
16 | font-weight: normal;
17 | }
18 | .form-signup .form-control {
19 | position: relative;
20 | height: auto;
21 | -webkit-box-sizing: border-box;
22 | -moz-box-sizing: border-box;
23 | box-sizing: border-box;
24 | padding: 10px;
25 | font-size: 16px;
26 | }
27 | .form-signup .form-control:focus {
28 | z-index: 2;
29 | }
30 | .form-signup input[type="username"] {
31 | margin-bottom: -1px;
32 | border-bottom-right-radius: 0;
33 | border-bottom-left-radius: 0;
34 | }
35 | .form-signup input[type="password"] {
36 | margin-bottom: -1px;
37 | border-bottom-right-radius: 0;
38 | border-bottom-left-radius: 0;
39 | }
40 | .form-signup input[type="email"] {
41 | margin-bottom: 10px;
42 | border-top-left-radius: 0;
43 | border-top-right-radius: 0;
44 | }
45 |
--------------------------------------------------------------------------------
/Web/signup/signup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | StorageStarted
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
34 |
35 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Web/signup/signup.js:
--------------------------------------------------------------------------------
1 | function signup() {
2 | // LeanCloud - 注册
3 | // https://leancloud.cn/docs/leanstorage_guide-js.html#hash885156
4 | LC.User.signUp({
5 | username: $("#inputUsername").val(),
6 | password: $("#inputPassword").val(),
7 | email: $("inputEmail").val(),
8 | })
9 | .then(() => {
10 | window.location.href = "./../products-list/products-list.html";
11 | })
12 | .catch(({ error }) => alert(error));
13 | }
14 |
15 | $(function () {
16 | $(".form-signup").on("submit", function (e) {
17 | e.preventDefault();
18 | signup();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/Web/storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/Web/storage.png
--------------------------------------------------------------------------------
/iOS/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | platform :ios, '9.0'
3 |
4 | target 'StorageStarted' do
5 | # Comment the next line if you don't want to use dynamic frameworks
6 | use_frameworks!
7 |
8 | pod 'LeanCloudObjc'
9 | pod 'SDWebImage'
10 |
11 | target 'StorageStartedTests' do
12 | inherit! :search_paths
13 | # Pods for testing
14 | end
15 |
16 | target 'StorageStartedUITests' do
17 | # Pods for testing
18 | end
19 |
20 | end
21 |
--------------------------------------------------------------------------------
/iOS/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - LeanCloudObjc (13.0.0):
3 | - LeanCloudObjc/Realtime (= 13.0.0)
4 | - LeanCloudObjc/Foundation (13.0.0)
5 | - "LeanCloudObjc/Protobuf+Protocol (13.0.0)"
6 | - LeanCloudObjc/Realtime (13.0.0):
7 | - LeanCloudObjc/Foundation (= 13.0.0)
8 | - "LeanCloudObjc/Protobuf+Protocol (= 13.0.0)"
9 | - SDWebImage (5.11.1):
10 | - SDWebImage/Core (= 5.11.1)
11 | - SDWebImage/Core (5.11.1)
12 |
13 | DEPENDENCIES:
14 | - LeanCloudObjc
15 | - SDWebImage
16 |
17 | SPEC REPOS:
18 | trunk:
19 | - LeanCloudObjc
20 | - SDWebImage
21 |
22 | SPEC CHECKSUMS:
23 | LeanCloudObjc: a896a7521061948c6b636567c466691e9c7abd05
24 | SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
25 |
26 | PODFILE CHECKSUM: 176bb1ae50f6c972679c2a5ab037fedd157041b3
27 |
28 | COCOAPODS: 1.10.1
29 |
--------------------------------------------------------------------------------
/iOS/StorageStarted.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/iOS/StorageStarted.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/iOS/StorageStarted.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // StorageStarted
4 | //
5 | // Created by cuiyiran on 16/9/14.
6 | // Copyright © 2016年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // StorageStarted
4 | //
5 | // Created by cuiyiran on 16/9/14.
6 | // Copyright © 2016年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import "LCLoginViewController.h"
11 | #import
12 |
13 | #define APP_ID @"OLoj899IwHYi787ClrImlr3k-gzGzoHsz"
14 | #define APP_KEY @"gkz35mRTqTE2aqwp7dEr5uEE"
15 | #define SERVER_URL @"https://oloj899i.lc-cn-n1-shared.com"
16 |
17 | @interface AppDelegate ()
18 |
19 | @end
20 |
21 | @implementation AppDelegate
22 |
23 |
24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
25 |
26 | //初始化 SDK
27 | [LCApplication setApplicationId:APP_ID clientKey:APP_KEY serverURLString:SERVER_URL];
28 | //开启调试日志
29 | [LCApplication setAllLogsEnabled:YES];
30 |
31 | self.window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
32 | self.window.rootViewController = [[LCLoginViewController alloc]init];
33 | [self.window makeKeyAndVisible];
34 |
35 | return YES;
36 | }
37 |
38 | - (void)applicationWillResignActive:(UIApplication *)application {
39 | // 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.
40 | // 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.
41 | }
42 |
43 | - (void)applicationDidEnterBackground:(UIApplication *)application {
44 | // 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.
45 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
46 | }
47 |
48 | - (void)applicationWillEnterForeground:(UIApplication *)application {
49 | // 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.
50 | }
51 |
52 | - (void)applicationDidBecomeActive:(UIApplication *)application {
53 | // 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.
54 | }
55 |
56 | - (void)applicationWillTerminate:(UIApplication *)application {
57 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
58 | }
59 |
60 | @end
61 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "3x",
16 | "size" : "20x20"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "2x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "3x",
26 | "size" : "29x29"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "2x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "3x",
36 | "size" : "40x40"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "2x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "iphone",
45 | "scale" : "3x",
46 | "size" : "60x60"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "1x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "2x",
56 | "size" : "20x20"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "20x20",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "29x29"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "29x29"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "40x40"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "40x40"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "1x",
86 | "size" : "76x76"
87 | },
88 | {
89 | "idiom" : "ipad",
90 | "scale" : "2x",
91 | "size" : "76x76"
92 | },
93 | {
94 | "idiom" : "ipad",
95 | "scale" : "2x",
96 | "size" : "83.5x83.5"
97 | },
98 | {
99 | "idiom" : "ios-marketing",
100 | "scale" : "1x",
101 | "size" : "1024x1024"
102 | }
103 | ],
104 | "info" : {
105 | "author" : "xcode",
106 | "version" : 1
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/downloadFailed.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "downloadFailed.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/downloadFailed.imageset/downloadFailed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/downloadFailed.imageset/downloadFailed.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/edit.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "edit.png",
6 | "compression-type" : "lossy",
7 | "scale" : "1x"
8 | },
9 | {
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | },
22 | "properties" : {
23 | "template-rendering-intent" : "original"
24 | }
25 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/edit.imageset/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/edit.imageset/edit.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/edit_cell.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "edit_cell.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/edit_cell.imageset/edit_cell.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/edit_cell.imageset/edit_cell.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/home.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "home.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "original"
23 | }
24 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/home.imageset/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/home.imageset/home.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/image_downloadFailed.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "image_downloadFailed.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/image_downloadFailed.imageset/image_downloadFailed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/image_downloadFailed.imageset/image_downloadFailed.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/not_logged_in.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "not_logged_in@2x.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "filename" : "not_logged_in@3x.png",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/not_logged_in.imageset/not_logged_in@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/not_logged_in.imageset/not_logged_in@2x.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/not_logged_in.imageset/not_logged_in@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/not_logged_in.imageset/not_logged_in@3x.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/personal.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "personal.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "original"
23 | }
24 | }
--------------------------------------------------------------------------------
/iOS/StorageStarted/Assets.xcassets/image/personal.imageset/personal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leancloud/StorageStarted/b5cb99d419f038e0ca2331ebbfb8779ebe94e88f/iOS/StorageStarted/Assets.xcassets/image/personal.imageset/personal.png
--------------------------------------------------------------------------------
/iOS/StorageStarted/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/EditProduct/EditProductViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // EditProductViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface EditProductViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/EditProduct/EditProductViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // EditProductViewController.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "EditProductViewController.h"
10 | #import
11 | #import
12 | #import
13 | @interface EditProductViewController ()
14 |
15 | @property (weak, nonatomic) IBOutlet UITextView *productitleText;
16 | @property (weak, nonatomic) IBOutlet UIImageView *productImageView;
17 | @property (weak, nonatomic) IBOutlet UITextField *priceLabel;
18 |
19 | @property (nonatomic,strong) UIImagePickerController *imagePicker;
20 | @property (nonatomic,strong) NSData * imageData;
21 | @end
22 |
23 | @implementation EditProductViewController
24 |
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 | self.navigationItem.title = @"发布新商品";
28 | }
29 | -(void)viewWillAppear:(BOOL)animated{
30 | [super viewWillAppear:YES];
31 |
32 | }
33 | - (void)viewWillDisappear:(BOOL)animated {
34 | [super viewWillDisappear:YES];
35 | }
36 | #pragma mark - Button Callbacks
37 |
38 | - (IBAction)openAlbumBtn:(id)sender {
39 | [self selectImageWithPickertype:UIImagePickerControllerSourceTypePhotoLibrary];
40 | }
41 |
42 | - (IBAction)takePhotoBtn:(id)sender {
43 | [self selectImageWithPickertype:UIImagePickerControllerSourceTypeCamera];
44 | }
45 | /*
46 | * LeanCloud - 保存对象 https://leancloud.cn/docs/leanstorage_guide-objc.html#hash632314851
47 | */
48 | - (IBAction)publishBtn:(id)sender {
49 | NSString *title = self.productitleText.text;
50 | NSNumber *price = @(self.priceLabel.text.intValue);
51 | LCObject *product = [LCObject objectWithClassName:@"Product"];
52 | [product setObject:title forKey:@"title"];
53 | [product setObject:price forKey:@"price"];
54 |
55 | // owner 字段为 Pointer 类型,指向 _User 表
56 | LCUser *currentUser = [LCUser currentUser];
57 | [product setObject:currentUser forKey:@"owner"];
58 |
59 | LCFile *file = [LCFile fileWithData:self.imageData];
60 | [product setObject:file forKey:@"image"];
61 | [product saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
62 | if (succeeded) {
63 | NSLog(@"保存新物品成功");
64 | [self alertMessage:@"保存新商品成功"];
65 |
66 | } else {
67 | NSLog(@"保存新物品出错 %@", error.localizedFailureReason);
68 | }
69 | }];
70 | }
71 | #pragma mark - UIImagePickerControllerDelegate
72 | #pragma mark - 拍照/选择图片结束
73 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
74 | {
75 | UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
76 | self.productImageView.image = image;
77 | NSData * imageData;
78 | if (UIImagePNGRepresentation(image)) {
79 | imageData = UIImagePNGRepresentation(image);
80 | }else{
81 | imageData = UIImageJPEGRepresentation(image, 1.0);
82 | }
83 | self.imageData = imageData;
84 |
85 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
86 | }
87 | #pragma mark - 取消拍照/选择图片
88 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
89 | {
90 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
91 | }
92 | #pragma mark - 选择图片
93 | -(void)selectImageWithPickertype:(UIImagePickerControllerSourceType)sourceType {
94 | if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
95 | self.imagePicker.delegate = self;
96 | self.imagePicker.allowsEditing = YES;
97 | self.imagePicker.sourceType = sourceType;
98 | [self presentViewController:self.imagePicker animated:YES completion:nil];
99 | }
100 | else{
101 | [self alertMessage:@"图片库不可用或当前设备没有摄像头"];
102 | }
103 | }
104 | #pragma mark - Private Methods
105 | -(void)alertMessage:(NSString *)message{
106 |
107 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:message preferredStyle:UIAlertControllerStyleAlert];
108 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
109 | [alertController addAction:action];
110 | [self presentViewController:alertController animated:YES completion:nil];
111 |
112 | }
113 | -(UIImagePickerController *)imagePicker{
114 | if (!_imagePicker) {
115 | _imagePicker = [[UIImagePickerController alloc]init];
116 | }
117 | return _imagePicker;
118 | }
119 | @end
120 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/EditProduct/EditProductViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
91 |
113 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/LoginOrSignup/LCLoginViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // LCLoginViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LCLoginViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/LoginOrSignup/LCLoginViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // LCLoginViewController.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "LCLoginViewController.h"
10 | #import "LCTabBarController.h"
11 | #import
12 | @interface LCLoginViewController ()
13 |
14 | @property (weak, nonatomic) IBOutlet UITextField *userNameTextField;
15 |
16 | @property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
17 |
18 | @end
19 |
20 | @implementation LCLoginViewController
21 |
22 | - (void)viewDidLoad {
23 | [super viewDidLoad];
24 |
25 | }
26 | -(void)viewWillAppear:(BOOL)animated{
27 | [super viewWillAppear:YES];
28 |
29 | }
30 | - (void)viewWillDisappear:(BOOL)animated {
31 | [super viewWillDisappear:YES];
32 | }
33 | // LeanCloud - 登录 https://leancloud.cn/docs/leanstorage_guide-objc.html#hash964666
34 | - (IBAction)LoginBtnClick:(id)sender {
35 |
36 | NSString *username = self.userNameTextField.text;
37 | NSString *password = self.passwordTextField.text;
38 | if (username && password) {
39 | [LCUser logInWithUsernameInBackground:username password:password block:^(LCUser *user, NSError *error){
40 | if (user) {
41 | [UIApplication sharedApplication].keyWindow.rootViewController = [[LCTabBarController alloc]init];
42 | } else {
43 | NSLog(@"登录失败:%@",error.localizedFailureReason);
44 | }
45 | }];
46 | }
47 | }
48 |
49 | // LeanCloud - 注册 https://leancloud.cn/docs/leanstorage_guide-objc.html#hash885156
50 | - (IBAction)SignUpBtnClick:(id)sender {
51 |
52 | LCUser *user = [LCUser user];
53 | user.username = self.userNameTextField.text;
54 | user.password = self.passwordTextField.text;
55 |
56 | [user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
57 | if (succeeded) {
58 | // 注册成功直接登录
59 | [LCUser logInWithUsernameInBackground:self.userNameTextField.text password:self.passwordTextField.text block:^(LCUser *user, NSError *error){
60 | if (user) {
61 | [UIApplication sharedApplication].keyWindow.rootViewController = [[LCTabBarController alloc]init];
62 | } else {
63 | NSLog(@"登录失败:%@",error.localizedFailureReason);
64 | }
65 | }];
66 | }else if(error.code == 202){
67 | //注册失败的原因可能有多种,常见的是用户名已经存在。
68 | NSLog(@"注册失败,用户名已经存在");
69 | }else{
70 | NSLog(@"注册失败:%@",error.localizedFailureReason);
71 | }
72 | }];
73 | }
74 |
75 | @end
76 |
77 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/LoginOrSignup/LCLoginViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
59 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/CALayer+XibBorderColor.h:
--------------------------------------------------------------------------------
1 | //
2 | // CALayer+XibBorderColor.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/24.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | @interface CALayer (XibBorderColor)
12 | - (void)setBorderColorWithUIColor:(UIColor *)color;
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/CALayer+XibBorderColor.m:
--------------------------------------------------------------------------------
1 | //
2 | // CALayer+XibBorderColor.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/24.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "CALayer+XibBorderColor.h"
10 | @implementation CALayer (XibBorderColor)
11 | - (void)setBorderColorWithUIColor:(UIColor *)color
12 | {
13 |
14 | self.borderColor = color.CGColor;
15 | }
16 | @end
17 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/LCNavigationController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface LCNavigationController : UINavigationController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/LCNavigationController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 | #import "LCNavigationController.h"
9 |
10 | @interface LCNavigationController ()
11 |
12 | @end
13 |
14 | @implementation LCNavigationController
15 |
16 | - (void)viewDidLoad {
17 | [super viewDidLoad];
18 | self.interactivePopGestureRecognizer.delegate = self;
19 |
20 | [self.navigationBar setBackgroundImage:[UIImage imageNamed:@"navigationbarBackgroundWhite"] forBarMetrics:UIBarMetricsDefault];
21 | }
22 |
23 | /**
24 | * 重写push方法:拦截所有push进来的自控制器
25 | *
26 | * @param viewController 刚刚push进来的自控制器
27 |
28 | */
29 | -(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
30 |
31 | if(self.childViewControllers.count>0){
32 | UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
33 | [backButton setImage:[UIImage imageNamed:@"navigationButtonReturn"] forState:UIControlStateNormal
34 | ];
35 | [backButton setImage:[UIImage imageNamed:@"navigationButtonReturnClick"] forState:UIControlStateHighlighted];
36 | [backButton setTitle:@"返回" forState:UIControlStateNormal];
37 | [backButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
38 | [backButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
39 |
40 | [backButton sizeToFit];
41 |
42 | // 这句代码放在sizeToFit后面
43 | backButton.contentEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0);
44 | [backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside ];
45 | viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:backButton];
46 | //隐藏底部工具条
47 | viewController.hidesBottomBarWhenPushed = YES;
48 | }
49 | [super pushViewController:viewController animated:YES];
50 | }
51 | - (void)back{
52 |
53 | [self popViewControllerAnimated:YES];
54 | }
55 |
56 |
57 | #pragma mark -
58 |
59 | - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
60 | {
61 | // 手势何时有效 : 当导航控制器的子控制器个数 > 1就有效
62 | return self.childViewControllers.count > 1;
63 | }
64 |
65 | @end
66 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/LCTabBarController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 | #import
9 |
10 | @interface LCTabBarController : UITabBarController
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/Other/LCTabBarController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "LCTabBarController.h"
10 | #import "LCNavigationController.h"
11 | #import "ProductListViewController.h"
12 | #import "EditProductViewController.h"
13 | #import "PersonalCenterViewController.h"
14 | @interface LCTabBarController ()
15 |
16 | @end
17 |
18 | @implementation LCTabBarController
19 |
20 | - (void)viewDidLoad {
21 | [super viewDidLoad];
22 |
23 | /**** 设置所有UITabBarItem的文字属性 ****/
24 | [self setupItemTitleTextAttributes];
25 |
26 | /**** 添加子控制器 ****/
27 | [self setupChildViewControllers];
28 | }
29 |
30 | -(void)setupChildViewControllers{
31 |
32 | [self setupOneChildController:[[LCNavigationController alloc]initWithRootViewController:[[ProductListViewController alloc] init]]title:@"商品列表" image:@"home"
33 | selectedImage:nil];
34 | [self setupOneChildController:[[LCNavigationController alloc]initWithRootViewController:[[EditProductViewController alloc] init] ]title:@"发布新商品" image:@"edit"
35 | selectedImage:nil];
36 |
37 | [self setupOneChildController:[[LCNavigationController alloc]initWithRootViewController:[[PersonalCenterViewController alloc] initWithNibName: @"PersonalCenterViewController" bundle:nil]]title:@"我的发布" image:@"personal"
38 | selectedImage:nil];
39 |
40 | }
41 | - (void)setupOneChildController:(UIViewController *)vc title:(NSString *)title image:(NSString *)image selectedImage:(NSString *)selectedImage
42 | {
43 | vc.tabBarItem.title = title;
44 | //如果没有传图片值,判断一下
45 | if(image.length){
46 | vc.tabBarItem.image = [UIImage imageNamed:image];
47 | vc.tabBarItem.selectedImage = [UIImage imageNamed:selectedImage];
48 | }
49 | [self addChildViewController:vc];
50 | }
51 |
52 | -(void)setupItemTitleTextAttributes{
53 | //UITabBarItem文字属性
54 | UITabBarItem *item = [UITabBarItem appearance];
55 | //文字正常
56 | NSMutableDictionary *normaAttrs = [NSMutableDictionary dictionary];
57 | normaAttrs[NSFontAttributeName] = [UIFont systemFontOfSize:12];
58 | normaAttrs[NSForegroundColorAttributeName] = [UIColor grayColor];
59 | [item setTitleTextAttributes: normaAttrs forState:UIControlStateNormal];
60 |
61 | //文字选中
62 | NSMutableDictionary *selectedAttrs = [NSMutableDictionary dictionary];
63 | selectedAttrs[NSForegroundColorAttributeName] = [UIColor blackColor];
64 | [item setTitleTextAttributes: selectedAttrs forState:UIControlStateSelected];
65 | }
66 | @end
67 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/MyProductCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // MyProductCell.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/24.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Product.h"
11 | @interface MyProductCell : UITableViewCell
12 |
13 | + (instancetype)cellWithTableView:(UITableView *)tableView;
14 |
15 | @property (nonatomic,strong) Product * product;
16 | @end
17 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/MyProductCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // MyProductCell.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/24.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "MyProductCell.h"
10 | #import
11 |
12 | @interface MyProductCell()
13 | @property (weak, nonatomic) IBOutlet UIImageView *productImageView;
14 | @property (weak, nonatomic) IBOutlet UILabel *title;
15 | @property (weak, nonatomic) IBOutlet UILabel *price;
16 |
17 | @end
18 | @implementation MyProductCell
19 |
20 | - (void)awakeFromNib {
21 | [super awakeFromNib];
22 | }
23 | + (instancetype)cellWithTableView:(UITableView *)tableView{
24 | static NSString * ProductListCellID = @"MyProductCell";
25 | MyProductCell *cell = [tableView dequeueReusableCellWithIdentifier:ProductListCellID];
26 | if(!cell){
27 | cell = [[[NSBundle mainBundle] loadNibNamed:@"MyProductCell" owner:nil options:nil] firstObject];
28 | }
29 | return cell;
30 | }
31 |
32 | -(void)setProduct:(Product *)product{
33 | _product = product;
34 | self.price.text = self.price.text = [NSString stringWithFormat:@"¥ %@",product.price];
35 | self.title.text = product.title;
36 | [self.productImageView sd_setImageWithURL:[NSURL URLWithString:product.productImageUrl]
37 | placeholderImage:[UIImage imageNamed:@"downloadFailed"]];
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/MyProductCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/PersonalCenterViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // PersonalCenterViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface PersonalCenterViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/PersonalCenterViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // PersonalCenterViewController.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "PersonalCenterViewController.h"
10 | #import "MyProductCell.h"
11 | #import "Product.h"
12 | #import
13 | #import "UpdateMyProductController.h"
14 | #import "LCLoginViewController.h"
15 |
16 | #import
17 | #import
18 | #import
19 |
20 | @interface PersonalCenterViewController ()
21 | @property (weak, nonatomic) IBOutlet UIImageView *userAvatarImage;
22 | @property (weak, nonatomic) IBOutlet UILabel *nameLabel;
23 | @property (weak, nonatomic) IBOutlet UITableView *productTableView;
24 | @property (weak, nonatomic) IBOutlet UILabel *noProductLabel;
25 |
26 | @property (nonatomic,strong) NSMutableArray *myProductArr;
27 | @property (nonatomic,strong) UIImagePickerController *imagePicker;
28 |
29 | @end
30 |
31 | @implementation PersonalCenterViewController
32 |
33 | - (void)viewDidLoad {
34 | [super viewDidLoad];
35 | self.navigationItem.title = @"个人中心";
36 | self.productTableView.delegate =self;
37 | self.productTableView.dataSource =self;
38 | [self setUserInfo];
39 | }
40 | -(void)viewWillAppear:(BOOL)animated{
41 | [super viewWillAppear:YES];
42 | //查询我发布的产品
43 | [self queryProduct];
44 |
45 | }
46 | - (void)viewWillDisappear:(BOOL)animated {
47 | [super viewWillDisappear:YES];
48 | }
49 | //退出登录
50 | - (IBAction)logOutBtnClick:(id)sender {
51 |
52 | [LCUser logOut];
53 | [UIApplication sharedApplication].keyWindow.rootViewController = [[LCLoginViewController alloc]init];
54 | }
55 | //更换头像
56 | - (IBAction)changeAvatarBtnClick:(id)sender {
57 | if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
58 | self.imagePicker.delegate = self;
59 | self.imagePicker.allowsEditing = YES;
60 | self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
61 | [self presentViewController:self.imagePicker animated:YES completion:nil];
62 | }
63 | else{
64 | [self alertMessage:@"图片库不可用"];
65 | }
66 | }
67 | #pragma mark - UITableViewDelegate
68 |
69 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
70 | return self.myProductArr.count;
71 | }
72 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
73 |
74 | MyProductCell * cell = [MyProductCell cellWithTableView:tableView];
75 | cell.product = self.myProductArr[indexPath.row];
76 | return cell;
77 | }
78 |
79 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
80 | return 48;
81 | }
82 | -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
83 |
84 | UpdateMyProductController *updateVc = [[UpdateMyProductController alloc]init];
85 | updateVc.product =self.myProductArr[indexPath.row];
86 | [self.navigationController pushViewController:updateVc animated:YES];
87 | }
88 |
89 | #pragma mark - UIImagePickerControllerDelegate
90 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
91 | {
92 | UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
93 | self.userAvatarImage.image =image;
94 | NSData * imageData = UIImagePNGRepresentation(image);
95 | LCUser *currentuser = [LCUser currentUser];
96 | LCFile *avatarFile = [LCFile fileWithData:imageData];
97 | [currentuser setObject:avatarFile forKey:@"avatar"];
98 | [currentuser saveInBackgroundWithBlock:^(BOOL succeeded, NSError * _Nullable error) {
99 | [self alertMessage:@"头像上传成功"];
100 | }];
101 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
102 |
103 | }
104 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
105 | {
106 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
107 | }
108 |
109 | #pragma mark - Life Cycle
110 | -(void)queryProduct{
111 | // 查询我发布过的产品
112 | LCUser *currentUser =[LCUser currentUser];
113 |
114 | LCQuery *query = [LCQuery queryWithClassName:@"Product"];
115 | [query orderByDescending:@"updateAt"];
116 | // owner 为 Pointer,指向 _User 表
117 | [query includeKey:@"owner"];
118 | // image 为 File
119 | [query includeKey:@"image"];
120 | [query whereKey:@"owner" equalTo:currentUser];
121 | query.limit = 20;
122 | [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
123 | if (!error) {
124 | [self.myProductArr removeAllObjects];
125 | for (NSDictionary *object in objects) {
126 | Product * product = [Product initWithObject:object];
127 | [self.myProductArr addObject:product];
128 | self.noProductLabel.hidden = (objects.count != 0);
129 |
130 | }
131 | }
132 | [self.productTableView reloadData];
133 | }];
134 | }
135 | -(void)setUserInfo{
136 | LCUser *currentUser =[LCUser currentUser];
137 | self.nameLabel.text = currentUser.username;
138 | LCFile * avatarFile = [currentUser objectForKey:@"avatar"];
139 | if (avatarFile) {
140 | [self.userAvatarImage sd_setImageWithURL:[NSURL URLWithString:avatarFile.url]
141 | placeholderImage:[UIImage imageNamed:@"not_logged_in"]];
142 | }
143 | }
144 | -(void)alertMessage:(NSString *)message{
145 |
146 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:message preferredStyle:UIAlertControllerStyleAlert];
147 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleCancel handler:nil];
148 | [alertController addAction:action];
149 | [self presentViewController:alertController animated:YES completion:nil];
150 |
151 | }
152 | -(NSMutableArray *)myProductArr{
153 | if (!_myProductArr) {
154 | _myProductArr = [NSMutableArray array];
155 | }
156 | return _myProductArr;
157 | }
158 | -(UIImagePickerController *)imagePicker{
159 | if (!_imagePicker) {
160 | _imagePicker = [[UIImagePickerController alloc]init];
161 | }
162 | return _imagePicker;
163 | }
164 | @end
165 |
166 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/PersonalCenterViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
56 |
63 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
96 |
97 |
98 |
99 |
100 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/UpdateMyProductController.h:
--------------------------------------------------------------------------------
1 | //
2 | // EditProductViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Product.h"
11 |
12 | @interface UpdateMyProductController : UIViewController
13 | @property (nonatomic,strong) Product * product;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/UpdateMyProductController.m:
--------------------------------------------------------------------------------
1 | //
2 | // EditProductViewController.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "UpdateMyProductController.h"
10 | #import
11 | #import
12 | #import
13 | @interface UpdateMyProductController ()
14 |
15 | @property (weak, nonatomic) IBOutlet UITextView *productitleText;
16 | @property (weak, nonatomic) IBOutlet UIImageView *productImageView;
17 | @property (weak, nonatomic) IBOutlet UITextField *priceLabel;
18 |
19 | @property (nonatomic,strong) UIImagePickerController *imagePicker;
20 | @property (nonatomic,strong) NSData * imageData;
21 | @end
22 |
23 | @implementation UpdateMyProductController
24 |
25 | - (void)viewDidLoad {
26 | [super viewDidLoad];
27 | self.navigationItem.title = @"修改我的商品";
28 | self.productitleText.text = self.product.title;
29 | self.priceLabel.text = self.product.price;
30 | [self.productImageView sd_setImageWithURL:[NSURL URLWithString:self.product.productImageUrl]
31 | placeholderImage:[UIImage imageNamed:@"downloadFailed"]];
32 | }
33 | -(void)viewWillAppear:(BOOL)animated{
34 | [super viewWillAppear:YES];
35 |
36 | }
37 | - (void)viewWillDisappear:(BOOL)animated {
38 | [super viewWillDisappear:YES];
39 | }
40 | #pragma mark - Button Callbacks
41 |
42 | - (IBAction)openAlbumBtn:(id)sender {
43 | [self selectImageWithPickertype:UIImagePickerControllerSourceTypePhotoLibrary];
44 | }
45 |
46 | - (IBAction)takePhotoBtn:(id)sender {
47 | [self selectImageWithPickertype:UIImagePickerControllerSourceTypeCamera];
48 | }
49 |
50 | /*
51 | * LeanCloud - 更新对象
52 | * https://leancloud.cn/docs/leanstorage_guide-objc.html#hash810954180
53 | */
54 | - (IBAction)publishBtn:(id)sender {
55 |
56 | NSString *title = self.productitleText.text;
57 | NSNumber *price = @(self.priceLabel.text.intValue);
58 | //根据 objectId 更新对象
59 | LCObject *product =[LCObject objectWithClassName:@"Product" objectId:self.product.objectId];
60 |
61 | [product setObject:title forKey:@"title"];
62 | [product setObject:price forKey:@"price"];
63 | if (self.imageData) {
64 | LCFile *file = [LCFile fileWithData:self.imageData];
65 | [product setObject:file forKey:@"image"];
66 | }
67 | [product saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
68 | if (succeeded) {
69 | NSLog(@"更新成功");
70 | [self .navigationController popToRootViewControllerAnimated: YES];
71 | } else {
72 | NSLog(@"更新出错 %@", error.localizedFailureReason);
73 | }
74 | }];
75 | }
76 | #pragma mark - UIImagePickerControllerDelegate
77 | #pragma mark - 拍照/选择图片结束
78 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
79 | {
80 | UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
81 | self.productImageView.image = image;
82 | NSData * imageData;
83 | if (UIImagePNGRepresentation(image)) {
84 | imageData = UIImagePNGRepresentation(image);
85 | }else{
86 | imageData = UIImageJPEGRepresentation(image, 1.0);
87 | }
88 | self.imageData = imageData;
89 |
90 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
91 | }
92 | #pragma mark - 取消拍照/选择图片
93 | - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
94 | {
95 | [self.imagePicker dismissViewControllerAnimated:YES completion:nil];
96 | }
97 | #pragma mark - 选择图片
98 | -(void)selectImageWithPickertype:(UIImagePickerControllerSourceType)sourceType {
99 | if ([UIImagePickerController isSourceTypeAvailable:sourceType]) {
100 | self.imagePicker.delegate = self;
101 | self.imagePicker.allowsEditing = YES;
102 | self.imagePicker.sourceType = sourceType;
103 | [self presentViewController:self.imagePicker animated:YES completion:nil];
104 | }
105 | else{
106 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"图片库不可用或当前设备没有摄像头" preferredStyle:UIAlertControllerStyleAlert];
107 | UIAlertAction *action = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
108 | [alertController addAction:action];
109 | [self presentViewController:alertController animated:YES completion:nil];
110 | }
111 | }
112 | -(UIImagePickerController *)imagePicker{
113 | if (!_imagePicker) {
114 | _imagePicker = [[UIImagePickerController alloc]init];
115 | }
116 | return _imagePicker;
117 | }
118 | @end
119 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/PersonalCenter/UpdateMyProductController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
32 |
38 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
91 |
113 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/Product.h:
--------------------------------------------------------------------------------
1 | //
2 | // Product.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | @interface Product : NSObject
12 |
13 | +(instancetype)initWithObject:(NSDictionary *)obj;
14 |
15 | @property (nonatomic,copy) NSString *objectId;
16 | /** 用户名 */
17 | @property (nonatomic,copy) NSString *name;
18 | /** 用户头像 */
19 | @property (nonatomic,copy) NSString * avatarUrl;
20 | /** 商品发布日期 */
21 | @property (nonatomic,copy) NSString *date;
22 | /** 商品价格 */
23 | @property (nonatomic,copy) NSString *price;
24 | /** 商品描述 */
25 | @property (nonatomic,copy) NSString *title;
26 | /** 商品图片 */
27 | @property (nonatomic,copy) NSString *productImageUrl;
28 | /** cell 的高度 */
29 | @property (nonatomic, assign) CGFloat cellHeight;
30 | @end
31 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/Product.m:
--------------------------------------------------------------------------------
1 | //
2 | // Product.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "Product.h"
10 | #import
11 | #import
12 | @implementation Product
13 | +(instancetype)initWithObject:(NSDictionary *)obj{
14 | Product * product = [[Product alloc] init];
15 | product.objectId =[obj objectForKey:@"objectId"];
16 | LCUser *owner =[obj objectForKey:@"owner"];
17 | product.name =owner.username;
18 | LCFile *userAvatar =[owner objectForKey:@"avatar"];
19 | if (userAvatar) {
20 | product.avatarUrl = userAvatar.url;
21 | }
22 | NSDate *createdAt = [obj objectForKey:@"createdAt"];
23 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
24 | [dateFormatter setDateFormat:@"yyyy-MM-dd"];
25 | product.date = [dateFormatter stringFromDate:createdAt];
26 |
27 | product.price = [NSString stringWithFormat:@"%@",[obj objectForKey:@"price"]];
28 | product.title = [obj objectForKey:@"title"];
29 |
30 | LCFile *file = [obj objectForKey:@"image"];
31 | product.productImageUrl = file.url;
32 |
33 | return product;
34 | }
35 | #pragma mark - 自适应cell高度
36 | -(CGFloat)cellHeight{
37 | //如果cell 的高度已经计算过,就直接返回
38 | if(_cellHeight) return _cellHeight;
39 |
40 | // cell高度 = 187+加文字高度
41 | _cellHeight = 187;
42 | CGSize labelSize =[self getSizeWithStr:self.title Width:[[UIScreen mainScreen] bounds].size.width-73 Font:11];
43 | _cellHeight+= labelSize.height;
44 | return _cellHeight;
45 | }
46 | - (CGSize) getSizeWithStr:(NSString *) str Width:(float)width Font:(float)fontSize
47 | {
48 | NSDictionary * attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:fontSize]};
49 | CGSize tempSize = [str boundingRectWithSize:CGSizeMake(width, MAXFLOAT)
50 | options:NSStringDrawingUsesLineFragmentOrigin
51 | attributes:attribute
52 | context:nil].size;
53 | return tempSize;
54 | }
55 | @end
56 |
57 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/ProductListCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListCell.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "Product.h"
11 | @interface ProductListCell : UITableViewCell
12 |
13 | + (instancetype)cellWithTableView:(UITableView *)tableView;
14 |
15 | @property (nonatomic,strong) Product * product;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/ProductListCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListCell.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "ProductListCell.h"
10 | #import
11 | @interface ProductListCell()
12 | @property (weak, nonatomic) IBOutlet UIImageView *avatarImageView;
13 | @property (weak, nonatomic) IBOutlet UILabel *name;
14 | @property (weak, nonatomic) IBOutlet UILabel *time;
15 | @property (weak, nonatomic) IBOutlet UILabel *price;
16 | @property (weak, nonatomic) IBOutlet UILabel *title;
17 | @property (weak, nonatomic) IBOutlet UIImageView *productImageView;
18 |
19 | @end
20 |
21 | @implementation ProductListCell
22 |
23 | - (void)awakeFromNib {
24 | [super awakeFromNib];
25 | }
26 |
27 | + (instancetype)cellWithTableView:(UITableView *)tableView{
28 | static NSString * ProductListCellID = @"ProductListCell";
29 | ProductListCell *cell = [tableView dequeueReusableCellWithIdentifier:ProductListCellID];
30 | if(!cell){
31 | cell = [[[NSBundle mainBundle] loadNibNamed:@"ProductListCell" owner:nil options:nil] firstObject];
32 | }
33 | cell.selectionStyle = UITableViewCellSelectionStyleNone;
34 | tableView.separatorStyle =UITableViewCellSeparatorStyleNone;
35 |
36 | return cell;
37 | }
38 |
39 | -(void)setProduct:(Product *)product{
40 | _product = product;
41 | self.name.text = product.name;
42 | self.time.text = product.date;
43 | self.price.text = [NSString stringWithFormat:@"¥ %@",product.price];
44 | self.title.text = product.title;
45 | [self.avatarImageView sd_setImageWithURL:[NSURL URLWithString:product.avatarUrl]
46 | placeholderImage:[UIImage imageNamed:@"not_logged_in"]];
47 | [self.productImageView sd_setImageWithURL:[NSURL URLWithString:product.productImageUrl]
48 | placeholderImage:[UIImage imageNamed:@"downloadFailed"]];
49 | }
50 | @end
51 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/ProductListCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
45 |
51 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/ProductListViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.h
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface ProductListViewController : UITableViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Classes/ProductList/ProductListViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ProductListViewController.m
3 | // StorageStarted
4 | //
5 | // Created by XiaoXu on 2018/7/23.
6 | // Copyright © 2018年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import "ProductListViewController.h"
10 | #import "Product.h"
11 | #import "ProductListCell.h"
12 | #import
13 | @interface ProductListViewController ()
14 | @property (nonatomic,strong) NSMutableArray *productArr;
15 | @end
16 |
17 | @implementation ProductListViewController
18 |
19 | - (void)viewDidLoad {
20 | [super viewDidLoad];
21 | self.navigationItem.title = @"LeanCloud";
22 | self.tableView.delegate =self;
23 | self.tableView.dataSource =self;
24 | }
25 | -(void)viewWillAppear:(BOOL)animated{
26 | [super viewWillAppear:YES];
27 | [self.productArr removeAllObjects];
28 | [self queryProduct];
29 |
30 | }
31 | - (void)viewWillDisappear:(BOOL)animated {
32 | [super viewWillDisappear:YES];
33 | }
34 |
35 | #pragma mark - Private Methods
36 | // LeanCloud - 查询 https://leancloud.cn/docs/leanstorage_guide-objc.html#hash860317
37 | -(void)queryProduct{
38 |
39 | LCQuery *query = [LCQuery queryWithClassName:@"Product"];
40 | [query orderByDescending:@"createdAt"];
41 | // owner 为 Pointer,指向 _User 表
42 | [query includeKey:@"owner"];
43 | // image 为 File
44 | [query includeKey:@"image"];
45 | query.limit = 20;
46 | [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
47 | if (!error) {
48 | for (NSDictionary *object in objects) {
49 | Product * product = [Product initWithObject:object];
50 | [self.productArr addObject:product];
51 | }
52 | }
53 | [self.tableView reloadData];
54 |
55 | }];
56 |
57 | }
58 | #pragma mark - UITableViewDelegate
59 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
60 | return self.productArr.count;
61 | }
62 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
63 |
64 | ProductListCell * cell = [ProductListCell cellWithTableView:tableView];
65 | cell.product = self.productArr[indexPath.row];
66 | return cell;
67 |
68 | }
69 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
70 | return self.productArr[indexPath.row].cellHeight;
71 | }
72 | -(NSMutableArray *)productArr{
73 | if (!_productArr) {
74 | _productArr =[NSMutableArray array];
75 | }
76 | return _productArr;
77 | }
78 |
79 | @end
80 |
81 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | NSPhotoLibraryUsageDescription
26 | 此 App 需要您的同意才能读取媒体资料库
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/iOS/StorageStarted/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // StorageStarted
4 | //
5 | // Created by cuiyiran on 16/9/14.
6 | // Copyright © 2016年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/iOS/StorageStartedTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/iOS/StorageStartedTests/StorageStartedTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // StorageStartedTests.m
3 | // StorageStartedTests
4 | //
5 | // Created by cuiyiran on 16/9/14.
6 | // Copyright © 2016年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface StorageStartedTests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation StorageStartedTests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 | // Put setup code here. This method is called before the invocation of each test method in the class.
20 | }
21 |
22 | - (void)tearDown {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | [super tearDown];
25 | }
26 |
27 | - (void)testExample {
28 | // This is an example of a functional test case.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | - (void)testPerformanceExample {
33 | // This is an example of a performance test case.
34 | [self measureBlock:^{
35 | // Put the code you want to measure the time of here.
36 | }];
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/iOS/StorageStartedUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/iOS/StorageStartedUITests/StorageStartedUITests.m:
--------------------------------------------------------------------------------
1 | //
2 | // StorageStartedUITests.m
3 | // StorageStartedUITests
4 | //
5 | // Created by cuiyiran on 16/9/14.
6 | // Copyright © 2016年 cuiyiran. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface StorageStartedUITests : XCTestCase
12 |
13 | @end
14 |
15 | @implementation StorageStartedUITests
16 |
17 | - (void)setUp {
18 | [super setUp];
19 |
20 | // Put setup code here. This method is called before the invocation of each test method in the class.
21 |
22 | // In UI tests it is usually best to stop immediately when a failure occurs.
23 | self.continueAfterFailure = NO;
24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
25 | [[[XCUIApplication alloc] init] launch];
26 |
27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
28 | }
29 |
30 | - (void)tearDown {
31 | // Put teardown code here. This method is called after the invocation of each test method in the class.
32 | [super tearDown];
33 | }
34 |
35 | - (void)testExample {
36 | // Use recording to get started writing UI tests.
37 | // Use XCTAssert and related functions to verify your tests produce the correct results.
38 | }
39 |
40 | @end
41 |
--------------------------------------------------------------------------------