Commit 302e829d authored by jinzhu's avatar jinzhu

merge master

parents 7715ba10 905aaafe
// Generated file. Do not edit.
def localProperties = new Properties()
def localPropertiesFile = new File(buildscript.sourceFile.parentFile.parentFile, 'local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.library'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
implementation 'com.android.support:support-v13:27.1.1'
implementation 'com.android.support:support-annotations:27.1.1'
}
<!-- Generated file. Do not edit. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gmalpha_flutter">
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
package io.flutter.facade;
import android.app.Activity;
import android.arch.lifecycle.Lifecycle;
import android.arch.lifecycle.LifecycleObserver;
import android.arch.lifecycle.OnLifecycleEvent;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import io.flutter.plugin.common.BasicMessageChannel;
import io.flutter.plugin.common.StringCodec;
import io.flutter.view.FlutterMain;
import io.flutter.view.FlutterNativeView;
import io.flutter.view.FlutterRunArguments;
import io.flutter.view.FlutterView;
import io.flutter.plugins.GeneratedPluginRegistrant;
/**
* Main entry point for using Flutter in Android applications.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public final class Flutter {
private Flutter() {
// to prevent instantiation
}
/**
* Initiates the Dart VM. Calling this method at an early point may help decreasing time to first
* frame for a subsequently created {@link FlutterView}.
*
* @param applicationContext the application's {@link Context}
*/
public static void startInitialization(@NonNull Context applicationContext) {
FlutterMain.startInitialization(applicationContext);
}
/**
* Creates a {@link FlutterFragment} managing a {@link FlutterView}. The optional
* initial route string will be made available to the Dart code
* (via {@code window.defaultRouteName}) and may be used to determine which widget
* should be displayed in the view. The default initialRoute is "/".
*
* @param initialRoute an initial route {@link String}, or null
* @return a {@link FlutterFragment}
*/
@NonNull
public static FlutterFragment createFragment(String initialRoute) {
final FlutterFragment fragment = new FlutterFragment();
final Bundle args = new Bundle();
args.putString(FlutterFragment.ARG_ROUTE, initialRoute);
fragment.setArguments(args);
return fragment;
}
/**
* Creates a {@link FlutterView} linked to the specified {@link Activity} and {@link Lifecycle}.
* The optional initial route string will be made available to the Dart code (via
* {@code window.defaultRouteName}) and may be used to determine which widget should be displayed
* in the view. The default initialRoute is "/".
*
* @param activity an {@link Activity}
* @param lifecycle a {@link Lifecycle}
* @param initialRoute an initial route {@link String}, or null
* @return a {@link FlutterView}
*/
@NonNull
public static FlutterView createView(@NonNull final Activity activity, @NonNull final Lifecycle lifecycle, final String initialRoute) {
FlutterMain.startInitialization(activity.getApplicationContext());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), null);
final FlutterNativeView nativeView = new FlutterNativeView(activity);
final FlutterView flutterView = new FlutterView(activity, null, nativeView) {
private final BasicMessageChannel<String> lifecycleMessages = new BasicMessageChannel<>(this, "flutter/lifecycle", StringCodec.INSTANCE);
@Override
public void onFirstFrame() {
super.onFirstFrame();
setAlpha(1.0f);
}
@Override
public void onPostResume() {
// Overriding default behavior to avoid dictating system UI via PlatformPlugin.
lifecycleMessages.send("AppLifecycleState.resumed");
}
};
if (initialRoute != null) {
flutterView.setInitialRoute(initialRoute);
}
lifecycle.addObserver(new LifecycleObserver() {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onCreate() {
final FlutterRunArguments arguments = new FlutterRunArguments();
arguments.bundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
arguments.entrypoint = "main";
flutterView.runFromBundle(arguments);
GeneratedPluginRegistrant.registerWith(flutterView.getPluginRegistry());
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
flutterView.onStart();
}
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
flutterView.onPostResume();
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void onPause() {
flutterView.onPause();
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
flutterView.onStop();
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onDestroy() {
flutterView.destroy();
}
});
flutterView.setAlpha(0.0f);
return flutterView;
}
}
package io.flutter.facade;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import io.flutter.view.FlutterView;
/**
* A {@link Fragment} managing a {@link FlutterView}.
*
* <p><strong>Warning:</strong> This file is auto-generated by Flutter tooling.
* DO NOT EDIT.</p>
*/
public class FlutterFragment extends Fragment {
public static final String ARG_ROUTE = "route";
private String mRoute = "/";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mRoute = getArguments().getString(ARG_ROUTE);
}
}
@Override
public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
super.onInflate(context, attrs, savedInstanceState);
}
@Override
public FlutterView onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return Flutter.createView(getActivity(), getLifecycle(), mRoute);
}
}
package io.flutter.plugins;
import io.flutter.plugin.common.PluginRegistry;
import com.taobao.idlefish.flutterboost.FlutterBoostPlugin;
import flutter.transer.wanmeizhensuo.com.native_flutter_transfer_plugin.NativeFlutterTransferPlugin;
import fleamarket.taobao.com.xservicekit.XserviceKitPlugin;
/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FlutterBoostPlugin.registerWith(registry.registrarFor("com.taobao.idlefish.flutterboost.FlutterBoostPlugin"));
NativeFlutterTransferPlugin.registerWith(registry.registrarFor("flutter.transer.wanmeizhensuo.com.native_flutter_transfer_plugin.NativeFlutterTransferPlugin"));
XserviceKitPlugin.registerWith(registry.registrarFor("fleamarket.taobao.com.xservicekit.XserviceKitPlugin"));
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
def flutterPluginVersion = 'managed'
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
defaultConfig {
applicationId "com.example.gmalpha_flutter.host"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
profile {
initWith debug
}
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
buildDir = new File(rootProject.projectDir, "../build/host")
dependencies {
implementation project(':flutter')
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:design:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.gmalpha_flutter.host">
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name="io.flutter.app.FlutterApplication"
android:label="gmalpha_flutter"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
package com.example.gmalpha_flutter.host;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>
// Generated file. Do not edit.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.1'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
org.gradle.jvmargs=-Xmx1536M
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
// Generated file. Do not edit.
def scriptFile = getClass().protectionDomain.codeSource.location.toURI()
def flutterProjectRoot = new File(scriptFile).parentFile.parentFile
gradle.include ':flutter'
gradle.project(':flutter').projectDir = new File(flutterProjectRoot, '.android/Flutter')
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot, '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.toPath().resolve(path).resolve('android').toFile()
gradle.include ":$name"
gradle.project(":$name").projectDir = pluginDirectory
}
gradle.getGradle().projectsLoaded { g ->
g.rootProject.beforeEvaluate { p ->
_mainModuleName = binding.variables['mainModuleName']
if (_mainModuleName != null && !_mainModuleName.empty) {
p.ext.mainModuleName = _mainModuleName
}
}
g.rootProject.afterEvaluate { p ->
p.subprojects { sp ->
if (sp.name != 'flutter') {
sp.evaluationDependsOn(':flutter')
}
}
}
}
sdk.dir=/Users/mac/Library/Android/sdk
flutter.sdk=/Users/mac/development/flutter
\ No newline at end of file
// Generated file. Do not edit.
include ':app'
rootProject.name = 'android_generated'
setBinding(new Binding([gradle: this]))
evaluate(new File('include_flutter.groovy'))
......@@ -35,7 +35,17 @@ xcuserdata
Icon?
.tags*
*.android/local.properties
#*.android/app/src/main/java/com/example/myflutter/
*..android/app/src/main/java/
build/
<<<<<<< HEAD
.android/
#.ios/
=======
# 暂时忽略不追踪 因为android 要跑起来需要更改很多配置
#.android/
*.android/local.properties
.ios/
>>>>>>> master
.flutter-plugins
/Users/mac/development/flutter/.pub-cache/git/flutter_boost-3978c43c31c1c29b569724fd330b367caa459acc
\ No newline at end of file
/Users/mac/development/flutter/.pub-cache/git/native_flutter_transfer_plugin-280d323852a6ba68678e5f9962dbe36d85526cee
\ No newline at end of file
/Users/mac/development/flutter/.pub-cache/hosted/pub.flutter-io.cn/xservice_kit-0.0.29
\ No newline at end of file
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/mac/development/flutter
FLUTTER_APPLICATION_PATH=/Users/mac/code/GMAlpha/gmalpha_flutter
FLUTTER_TARGET=/Users/mac/code/GMAlpha/gmalpha_flutter/lib/main.dart
FLUTTER_APPLICATION_PATH=/Users/mac/code/gmalpha_flutter
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
SYMROOT=${SOURCE_ROOT}/../build/ios
FLUTTER_BUILD_NAME=1.0.0
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTER_H_
#define FLUTTER_FLUTTER_H_
/**
BREAKING CHANGES:
December 17, 2018:
- Changed designated initializer on FlutterEngine
October 5, 2018:
- Removed FlutterNavigationController.h/.mm
- Changed return signature of `FlutterDartHeadlessCodeRunner.run*` from void
to bool
- Removed HeadlessPlatformViewIOS
- Marked FlutterDartHeadlessCodeRunner deprecated
August 31, 2018: Marked -[FlutterDartProject
initFromDefaultSourceForConfiguration] and FlutterStandardBigInteger as
unavailable.
July 26, 2018: Marked -[FlutterDartProject
initFromDefaultSourceForConfiguration] deprecated.
February 28, 2018: Removed "initWithFLXArchive" and
"initWithFLXArchiveWithScriptSnapshot".
January 15, 2018: Marked "initWithFLXArchive" and
"initWithFLXArchiveWithScriptSnapshot" as unavailable following the
deprecation from December 11, 2017. Scheduled to be removed on February
19, 2018.
January 09, 2018: Deprecated "FlutterStandardBigInteger" and its use in
"FlutterStandardMessageCodec" and "FlutterStandardMethodCodec". Scheduled to
be marked as unavailable once the deprecation has been available on the
flutter/flutter alpha branch for four weeks. "FlutterStandardBigInteger" was
needed because the Dart 1.0 int type had no size limit. With Dart 2.0, the
int type is a fixed-size, 64-bit signed integer. If you need to communicate
larger integers, use NSString encoding instead.
December 11, 2017: Deprecated "initWithFLXArchive" and
"initWithFLXArchiveWithScriptSnapshot" and scheculed the same to be marked as
unavailable on January 15, 2018. Instead, "initWithFlutterAssets" and
"initWithFlutterAssetsWithScriptSnapshot" should be used. The reason for this
change is that the FLX archive will be deprecated and replaced with a flutter
assets directory containing the same files as the FLX did.
November 29, 2017: Added a BREAKING CHANGES section.
*/
#include "FlutterAppDelegate.h"
#include "FlutterBinaryMessenger.h"
#include "FlutterCallbackCache.h"
#include "FlutterChannels.h"
#include "FlutterCodecs.h"
#include "FlutterDartProject.h"
#include "FlutterEngine.h"
#include "FlutterHeadlessDartRunner.h"
#include "FlutterMacros.h"
#include "FlutterPlatformViews.h"
#include "FlutterPlugin.h"
#include "FlutterPluginAppLifeCycleDelegate.h"
#include "FlutterTexture.h"
#include "FlutterViewController.h"
#endif // FLUTTER_FLUTTER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERAPPDELEGATE_H_
#define FLUTTER_FLUTTERAPPDELEGATE_H_
#import <UIKit/UIKit.h>
#include "FlutterMacros.h"
#include "FlutterPlugin.h"
/**
* `UIApplicationDelegate` subclass for simple apps that want default behavior.
*
* This class implements the following behaviors:
* * Status bar touches are forwarded to the key window's root view
* `FlutterViewController`, in order to trigger scroll to top.
* * Keeps the Flutter connection open in debug mode when the phone screen
* locks.
*
* App delegates for Flutter applications are *not* required to inherit from
* this class. Developers of custom app delegate classes should copy and paste
* code as necessary from FlutterAppDelegate.mm.
*/
FLUTTER_EXPORT
@interface FlutterAppDelegate
: UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
@property(strong, nonatomic) UIWindow* window;
@end
#endif // FLUTTER_FLUTTERDARTPROJECT_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERBINARYMESSENGER_H_
#define FLUTTER_FLUTTERBINARYMESSENGER_H_
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
NS_ASSUME_NONNULL_BEGIN
/**
* A message reply callback.
*
* Used for submitting a binary reply back to a Flutter message sender. Also used
* in for handling a binary message reply received from Flutter.
*
* @param reply The reply.
*/
typedef void (^FlutterBinaryReply)(NSData* _Nullable reply);
/**
* A strategy for handling incoming binary messages from Flutter and to send
* asynchronous replies back to Flutter.
*
* @param message The message.
* @param reply A callback for submitting an asynchronous reply to the sender.
*/
typedef void (^FlutterBinaryMessageHandler)(NSData* _Nullable message, FlutterBinaryReply reply);
/**
* A facility for communicating with the Flutter side using asynchronous message
* passing with binary messages.
*
* Implementated by:
* - `FlutterBasicMessageChannel`, which supports communication using structured
* messages.
* - `FlutterMethodChannel`, which supports communication using asynchronous
* method calls.
* - `FlutterEventChannel`, which supports commuication using event streams.
*/
FLUTTER_EXPORT
@protocol FlutterBinaryMessenger <NSObject>
/**
* Sends a binary message to the Flutter side on the specified channel, expecting
* no reply.
*
* @param channel The channel name.
* @param message The message.
*/
- (void)sendOnChannel:(NSString*)channel message:(NSData* _Nullable)message;
/**
* Sends a binary message to the Flutter side on the specified channel, expecting
* an asynchronous reply.
*
* @param channel The channel name.
* @param message The message.
* @param callback A callback for receiving a reply.
*/
- (void)sendOnChannel:(NSString*)channel
message:(NSData* _Nullable)message
binaryReply:(FlutterBinaryReply _Nullable)callback
// TODO: Add macOS support for replies once
// https://github.com/flutter/flutter/issues/18852 is fixed.
API_UNAVAILABLE(macos);
/**
* Registers a message handler for incoming binary messages from the Flutter side
* on the specified channel.
*
* Replaces any existing handler. Use a `nil` handler for unregistering the
* existing handler.
*
* @param channel The channel name.
* @param handler The message handler.
*/
- (void)setMessageHandlerOnChannel:(NSString*)channel
binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERBINARYMESSENGER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERCALLBACKCACHE_H_
#define FLUTTER_FLUTTERCALLBACKCACHE_H_
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
/**
* An object containing the result of `FlutterCallbackCache`'s `lookupCallbackInformation`
* method.
*/
FLUTTER_EXPORT
@interface FlutterCallbackInformation : NSObject
/**
* The name of the callback.
*/
@property(retain) NSString* callbackName;
/**
* The class name of the callback.
*/
@property(retain) NSString* callbackClassName;
/**
* The library path of the callback.
*/
@property(retain) NSString* callbackLibraryPath;
@end
/**
* The cache containing callback information for spawning a
* `FlutterHeadlessDartRunner`.
*/
FLUTTER_EXPORT
@interface FlutterCallbackCache : NSObject
/**
* Returns the callback information for the given callback handle.
* This callback information can be used when spawning a
* `FlutterHeadlessDartRunner`.
*
* @param handle The handle for a callback, provided by the
* Dart method `PluginUtilities.getCallbackHandle`.
* @return A `FlutterCallbackInformation` object which contains the name of the
* callback, the name of the class in which the callback is defined, and the
* path of the library which contains the callback. If the provided handle is
* invalid, nil is returned.
*/
+ (FlutterCallbackInformation*)lookupCallbackInformation:(int64_t)handle;
@end
#endif // FLUTTER_FLUTTERCALLBACKCACHE_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERDARTPROJECT_H_
#define FLUTTER_FLUTTERDARTPROJECT_H_
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
/**
* A set of Flutter and Dart assets used by a `FlutterEngine` to initialize execution.
*/
FLUTTER_EXPORT
@interface FlutterDartProject : NSObject
/**
* Initializes a Flutter Dart project from a bundle.
*/
- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle NS_DESIGNATED_INITIALIZER;
/**
* Unavailable - use `init` instead.
*/
- (instancetype)initFromDefaultSourceForConfiguration FLUTTER_UNAVAILABLE("Use -init instead.");
/**
* Returns the file name for the given asset. If the bundle with the identifier
* "io.flutter.flutter.app" exists, it will try use that bundle; otherwise, it
* will use the main bundle. To specify a different bundle, use
* `-lookupKeyForAsset:asset:fromBundle`.
*
* @param asset The name of the asset. The name can be hierarchical.
* @return the file name to be used for lookup in the main bundle.
*/
+ (NSString*)lookupKeyForAsset:(NSString*)asset;
/**
* Returns the file name for the given asset.
* The returned file name can be used to access the asset in the supplied bundle.
*
* @param asset The name of the asset. The name can be hierarchical.
* @param bundle The `NSBundle` to use for looking up the asset.
* @return the file name to be used for lookup in the main bundle.
*/
+ (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(NSBundle*)bundle;
/**
* Returns the file name for the given asset which originates from the specified package.
* The returned file name can be used to access the asset in the application's main bundle.
*
* @param asset The name of the asset. The name can be hierarchical.
* @param package The name of the package from which the asset originates.
* @return the file name to be used for lookup in the main bundle.
*/
+ (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package;
/**
* Returns the file name for the given asset which originates from the specified package.
* The returned file name can be used to access the asset in the specified bundle.
*
* @param asset The name of the asset. The name can be hierarchical.
* @param package The name of the package from which the asset originates.
* @param bundle The bundle to use when doing the lookup.
* @return the file name to be used for lookup in the main bundle.
*/
+ (NSString*)lookupKeyForAsset:(NSString*)asset
fromPackage:(NSString*)package
fromBundle:(NSBundle*)bundle;
/**
* Returns the default identifier for the bundle where we expect to find the Flutter Dart
* application.
*/
+ (NSString*)defaultBundleIdentifier;
@end
#endif // FLUTTER_FLUTTERDARTPROJECT_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERHEADLESSDARTRUNNER_H_
#define FLUTTER_FLUTTERHEADLESSDARTRUNNER_H_
#import <Foundation/Foundation.h>
#include "FlutterBinaryMessenger.h"
#include "FlutterDartProject.h"
#include "FlutterEngine.h"
#include "FlutterMacros.h"
/**
* A callback for when FlutterHeadlessDartRunner has attempted to start a Dart
* Isolate in the background.
*
* @param success YES if the Isolate was started and run successfully, NO
* otherwise.
*/
typedef void (^FlutterHeadlessDartRunnerCallback)(BOOL success);
/**
* The FlutterHeadlessDartRunner runs Flutter Dart code with a null rasterizer,
* and no native drawing surface. It is appropriate for use in running Dart
* code e.g. in the background from a plugin.
*
* Most callers should prefer using `FlutterEngine` directly; this interface exists
* for legacy support.
*/
FLUTTER_EXPORT
FLUTTER_DEPRECATED("FlutterEngine should be used rather than FlutterHeadlessDartRunner")
@interface FlutterHeadlessDartRunner : FlutterEngine
/**
* Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`.
*
* If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate
* the project in a default location.
*
* A newly initialized engine will not run the `FlutterDartProject` until either
* `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called.
*
* @param labelPrefix The label prefix used to identify threads for this instance. Should
* be unique across FlutterEngine instances
* @param projectOrNil The `FlutterDartProject` to run.
*/
- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)projectOrNil;
/**
* Iniitalize this FlutterHeadlessDartRunner with a `FlutterDartProject`.
*
* If the FlutterDartProject is not specified, the FlutterHeadlessDartRunner will attempt to locate
* the project in a default location.
*
* A newly initialized engine will not run the `FlutterDartProject` until either
* `-runWithEntrypoint:` or `-runWithEntrypoint:libraryURI` is called.
*
* @param labelPrefix The label prefix used to identify threads for this instance. Should
* be unique across FlutterEngine instances
* @param projectOrNil The `FlutterDartProject` to run.
* @param allowHeadlessExecution Must be set to `YES`.
*/
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FlutterDartProject*)projectOrNil
allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER;
/**
* Not recommended for use - will initialize with a default label ("io.flutter.headless")
* and the default FlutterDartProject.
*/
- (instancetype)init;
@end
#endif // FLUTTER_FLUTTERHEADLESSDARTRUNNER_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERMACROS_H_
#define FLUTTER_FLUTTERMACROS_H_
#if defined(FLUTTER_FRAMEWORK)
#define FLUTTER_EXPORT __attribute__((visibility("default")))
#else // defined(FLUTTER_SDK)
#define FLUTTER_EXPORT
#endif // defined(FLUTTER_SDK)
#ifndef NS_ASSUME_NONNULL_BEGIN
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end")
#endif // defined(NS_ASSUME_NONNULL_BEGIN)
/**
* Indicates that the API has been deprecated for the specified reason. Code
* that uses the deprecated API will continue to work as before. However, the
* API will soon become unavailable and users are encouraged to immediately take
* the appropriate action mentioned in the deprecation message and the BREAKING
* CHANGES section present in the Flutter.h umbrella header.
*/
#define FLUTTER_DEPRECATED(msg) __attribute__((__deprecated__(msg)))
/**
* Indicates that the previously deprecated API is now unavailable. Code that
* uses the API will not work and the declaration of the API is only a stub
* meant to display the given message detailing the actions for the user to take
* immediately.
*/
#define FLUTTER_UNAVAILABLE(msg) __attribute__((__unavailable__(msg)))
#endif // FLUTTER_FLUTTERMACROS_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERPLATFORMVIEWS_H_
#define FLUTTER_FLUTTERPLATFORMVIEWS_H_
#import <UIKit/UIKit.h>
#import "FlutterCodecs.h"
#import "FlutterMacros.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Wraps a `UIView` for embedding in the Flutter hierarchy
*/
@protocol FlutterPlatformView <NSObject>
/**
* Returns a reference to the `UIView` that is wrapped by this `FlutterPlatformView`.
*/
- (UIView*)view;
@end
FLUTTER_EXPORT
@protocol FlutterPlatformViewFactory <NSObject>
/**
* Create a `FlutterPlatformView`.
*
* Implemented by iOS code that expose a `UIView` for embedding in a Flutter app.
*
* The implementation of this method should create a new `UIView` and return it.
*
* @param frame The rectangle for the newly created `UIView` measued in points.
* @param viewId A unique identifier for this `UIView`.
* @param args Parameters for creating the `UIView` sent from the Dart side of the Flutter app.
* If `createArgsCodec` is not implemented, or if no creation arguments were sent from the Dart
* code, this will be null. Otherwise this will be the value sent from the Dart code as decoded by
* `createArgsCodec`.
*/
- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args;
/**
* Returns the `FlutterMessageCodec` for decoding the args parameter of `createWithFrame`.
*
* Only needs to be implemented if `createWithFrame` needs an arguments parameter.
*/
@optional
- (NSObject<FlutterMessageCodec>*)createArgsCodec;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERPLATFORMVIEWS_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
#define FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
#include "FlutterPlugin.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Propagates `UIAppDelegate` callbacks to registered plugins.
*/
FLUTTER_EXPORT
@interface FlutterPluginAppLifeCycleDelegate : NSObject
/**
* Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate
* as long as it is alive.
*
* `delegate` will only referenced weakly.
*/
- (void)addDelegate:(NSObject<FlutterPlugin>*)delegate;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*
* @return `NO` if any plugin vetoes application launch.
*/
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*
* @return `NO` if any plugin vetoes application launch.
*/
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationDidBecomeActive:(UIApplication*)application;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillResignActive:(UIApplication*)application;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationDidEnterBackground:(UIApplication*)application;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillEnterForeground:(UIApplication*)application;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)applicationWillTerminate:(UIApplication*)application;
/**
* Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- (void)application:(UIApplication*)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings;
#pragma GCC diagnostic pop
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
didReceiveLocalNotification:(UILocalNotification*)notification;
/**
* Calls all plugins registered for `UNUserNotificationCenterDelegate` callbacks.
*/
- (void)userNotificationCenter:(UNUserNotificationCenter*)center
willPresentNotification:(UNNotification*)notification
withCompletionHandler:
(void (^)(UNNotificationPresentationOptions options))completionHandler
API_AVAILABLE(ios(10));
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @return `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @return `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @return `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
openURL:(NSURL*)url
sourceApplication:(NSString*)sourceApplication
annotation:(id)annotation;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
- (void)application:(UIApplication*)application
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler
API_AVAILABLE(ios(9.0));
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @return `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
completionHandler:(nonnull void (^)(void))completionHandler;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @returns `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
/**
* Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
* some plugin handles the request.
*
* @return `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
continueUserActivity:(NSUserActivity*)userActivity
restorationHandler:(void (^)(NSArray*))restorationHandler;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERTEXTURE_H_
#define FLUTTER_FLUTTERTEXTURE_H_
#import <CoreMedia/CoreMedia.h>
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
NS_ASSUME_NONNULL_BEGIN
FLUTTER_EXPORT
@protocol FlutterTexture <NSObject>
- (CVPixelBufferRef _Nullable)copyPixelBuffer;
@end
FLUTTER_EXPORT
@protocol FlutterTextureRegistry <NSObject>
- (int64_t)registerTexture:(NSObject<FlutterTexture>*)texture;
- (void)textureFrameAvailable:(int64_t)textureId;
- (void)unregisterTexture:(int64_t)textureId;
@end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERTEXTURE_H_
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERVIEWCONTROLLER_H_
#define FLUTTER_FLUTTERVIEWCONTROLLER_H_
#import <UIKit/UIKit.h>
#include <sys/cdefs.h>
#include "FlutterBinaryMessenger.h"
#include "FlutterDartProject.h"
#include "FlutterEngine.h"
#include "FlutterMacros.h"
#include "FlutterPlugin.h"
#include "FlutterTexture.h"
@class FlutterEngine;
/**
* The name used for semantic update nofications via `NSNotificationCenter`.
*
* The object passed as the sender is the `FlutterViewController` associated
* with the update.
*/
FLUTTER_EXPORT
extern NSNotificationName const FlutterSemanticsUpdateNotification;
/**
* A `UIViewController` implementation for Flutter views.
*
* Dart execution, channel communication, texture registration, and plugin registration
* are all handled by `FlutterEngine`. Calls on this class to those members all proxy
* through to the `FlutterEngine` attached FlutterViewController.
*
* A FlutterViewController can be initialized either with an already-running `FlutterEngine`,
* or it can be initialized with a `FlutterDartProject` that will be used to spin up
* a new `FlutterEngine`. Developers looking to present and hide FlutterViewControllers
* in native iOS applications will usually want to maintain the `FlutterEngine` instance
* so as not to lose Dart-related state and asynchronous tasks when navigating back and
* forth between a FlutterViewController and other `UIViewController`s.
*/
FLUTTER_EXPORT
@interface FlutterViewController
: UIViewController <FlutterBinaryMessenger, FlutterTextureRegistry, FlutterPluginRegistry>
/**
* Initializes this FlutterViewController with the specified `FlutterEngine`.
*
* The initialized viewcontroller will attach itself to the engine as part of this process.
*
* @param engine The `FlutterEngine` instance to attach to.
* @param nibNameOrNil The NIB name to initialize this UIViewController with.
* @param nibBundleOrNil The NIB bundle.
*/
- (instancetype)initWithEngine:(FlutterEngine*)engine
nibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
/**
* Initializes a new FlutterViewController and `FlutterEngine` with the specified
* `FlutterDartProject`.
*
* @param projectOrNil The `FlutterDartProject` to initialize the `FlutterEngine` with.
* @param nibNameOrNil The NIB name to initialize this UIViewController with.
* @param nibBundleOrNil The NIB bundle.
*/
- (instancetype)initWithProject:(FlutterDartProject*)projectOrNil
nibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
- (void)handleStatusBarTouches:(UIEvent*)event;
/**
* Registers a callback that will be invoked when the Flutter view has been rendered.
* The callback will be fired only once.
*
* Replaces an existing callback. Use a `nil` callback to unregister the existing one.
*/
- (void)setFlutterViewDidRenderCallback:(void (^)(void))callback;
/**
* Returns the file name for the given asset.
* The returned file name can be used to access the asset in the application's
* main bundle.
*
* @param asset The name of the asset. The name can be hierarchical.
* @return The file name to be used for lookup in the main bundle.
*/
- (NSString*)lookupKeyForAsset:(NSString*)asset;
/**
* Returns the file name for the given asset which originates from the specified
* package.
* The returned file name can be used to access the asset in the application's
* main bundle.
*
* @param asset The name of the asset. The name can be hierarchical.
* @param package The name of the package from which the asset originates.
* @return The file name to be used for lookup in the main bundle.
*/
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package;
/**
* Sets the first route that the Flutter app shows. The default is "/".
* This method will guarnatee that the initial route is delivered, even if the
* Flutter window hasn't been created yet when called. It cannot be used to update
* the current route being shown in a visible FlutterViewController (see pushRoute
* and popRoute).
*
* @param route The name of the first route to show.
*/
- (void)setInitialRoute:(NSString*)route;
/**
* Instructs the Flutter Navigator (if any) to go back.
*/
- (void)popRoute;
/**
* Instructs the Flutter Navigator (if any) to push a route on to the navigation
* stack. The setInitialRoute method should be prefered if this is called before the
* FlutterViewController has come into view.
*
* @param route The name of the route to push to the navigation stack.
*/
- (void)pushRoute:(NSString*)route;
/**
* The `FlutterPluginRegistry` used by this FlutterViewController.
*/
- (id<FlutterPluginRegistry>)pluginRegistry;
/**
* Specifies the view to use as a splash screen. Flutter's rendering is asynchronous, so the first
* frame rendered by the Flutter application might not immediately appear when theFlutter view is
* initially placed in the view hierarchy. The splash screen view will be used as
* a replacement until the first frame is rendered.
*
* The view used should be appropriate for multiple sizes; an autoresizing mask to
* have a flexible width and height will be applied automatically.
*/
@property(strong, nonatomic) UIView* splashScreenView;
/**
* Attempts to set the `splashScreenView` property from the `UILaunchStoryboardName` from the
* main bundle's `Info.plist` file. This method will not change the value of `splashScreenView`
* if it cannot find a default one from a storyboard or nib.
*
* @return `YES` if successful, `NO` otherwise.
*/
- (BOOL)loadDefaultSplashScreenView;
/**
* Controls whether the created view will be opaque or not.
*
* Default is `YES`. Note that setting this to `NO` may negatively impact performance
* when using hardware acceleration, and toggling this will trigger a re-layout of the
* view.
*/
@property(nonatomic, getter=isViewOpaque) BOOL viewOpaque;
/**
* The `FlutterEngine` instance for this view controller.
*/
@property(weak, nonatomic, readonly) FlutterEngine* engine;
@end
#endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>Flutter</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Flutter</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
</dict>
</plist>
framework module Flutter {
umbrella header "Flutter.h"
export *
module * { export * }
}
#
# NOTE: This podspec is NOT to be published. It is only used as a local source!
#
Pod::Spec.new do |s|
s.name = 'Flutter'
s.version = '1.0.0'
s.summary = 'High-performance, high-fidelity mobile apps.'
s.description = <<-DESC
Flutter provides an easy and productive way to build and deploy high-performance mobile apps for Android and iOS.
DESC
s.homepage = 'https://flutter.io'
s.license = { :type => 'MIT' }
s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' }
s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s }
s.ios.deployment_target = '7.0'
s.vendored_frameworks = 'Flutter.framework'
end
platform :ios, '8.0'
#open source
source 'https://github.com/CocoaPods/Specs.git'
#our company
source 'git@git.wanmeizhensuo.com:gengmeiios/GMSpecs.git'
target 'Runner' do
# pod 'TMCache'
# pod 'Masonry'
# pod 'SDWebImage'
# pod 'GMCache'
# pod 'GMKit'
# pod 'GMPhobos'
flutter_application_path = '../'
eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
end
PODS:
- Flutter (1.0.0)
- flutter_boost (0.0.1):
- Flutter
- xservice_kit
- FlutterPluginRegistrant (0.0.1):
- Flutter
- flutter_boost
- native_flutter_transfer_plugin
- xservice_kit
- GMCache (0.2.3):
- TMCache (= 2.1.0)
- GMKit (1.1.3):
- GMKit/Category (= 1.1.3)
- GMKit/Color (= 1.1.3)
- GMKit/Constant (= 1.1.3)
- GMKit/FDFullscreenPopGesture (= 1.1.3)
- GMKit/Kit (= 1.1.3)
- GMKit/Protocol (= 1.1.3)
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/Category (1.1.3):
- GMKit/Color (= 1.1.3)
- GMKit/Constant (= 1.1.3)
- GMKit/Protocol (= 1.1.3)
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/Color (1.1.3):
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/Constant (1.1.3):
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/FDFullscreenPopGesture (1.1.3):
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/Kit (1.1.3):
- GMKit/Category (= 1.1.3)
- GMKit/Color (= 1.1.3)
- GMKit/Constant (= 1.1.3)
- GMKit/Protocol (= 1.1.3)
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMKit/Protocol (1.1.3):
- Masonry (= 1.1.0)
- SDWebImage (= 3.7.6)
- GMPhobos (1.2.2):
- GMCache (= 0.2.3)
- GMKit
- Masonry (1.1.0)
- native_flutter_transfer_plugin (0.0.1):
- Flutter
- GMCache
- GMKit (= 1.1.3)
- GMPhobos (= 1.2.2)
- Masonry
- SDWebImage
- TMCache
- SDWebImage (3.7.6):
- SDWebImage/Core (= 3.7.6)
- SDWebImage/Core (3.7.6)
- TMCache (2.1.0)
- xservice_kit (0.0.1):
- Flutter
DEPENDENCIES:
- Flutter (from `../.ios/Flutter/engine`)
- flutter_boost (from `../.ios/Flutter/.symlinks/flutter_boost/ios`)
- FlutterPluginRegistrant (from `../.ios/Flutter/FlutterPluginRegistrant`)
- native_flutter_transfer_plugin (from `../.ios/Flutter/.symlinks/native_flutter_transfer_plugin/ios`)
- xservice_kit (from `../.ios/Flutter/.symlinks/xservice_kit/ios`)
SPEC REPOS:
"git@git.wanmeizhensuo.com:gengmeiios/GMSpecs.git":
- GMCache
- GMKit
- GMPhobos
https://github.com/cocoapods/specs.git:
- Masonry
- SDWebImage
- TMCache
EXTERNAL SOURCES:
Flutter:
:path: "../.ios/Flutter/engine"
flutter_boost:
:path: "../.ios/Flutter/.symlinks/flutter_boost/ios"
FlutterPluginRegistrant:
:path: "../.ios/Flutter/FlutterPluginRegistrant"
native_flutter_transfer_plugin:
:path: "../.ios/Flutter/.symlinks/native_flutter_transfer_plugin/ios"
xservice_kit:
:path: "../.ios/Flutter/.symlinks/xservice_kit/ios"
SPEC CHECKSUMS:
Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a
flutter_boost: 0e85ea37c74ed39ee7b91a35612afa1605557484
FlutterPluginRegistrant: bd1540f14a5d82de17e722f30647919e115a4479
GMCache: 09a3029c96fe130e3a21faef70b3d9d2ce92d639
GMKit: 35f788243cceeddf3e13c5226b3ea0b5e08e2117
GMPhobos: 1ae96f00a847dde2ce813d5e232b47760677fe62
Masonry: 678fab65091a9290e40e2832a55e7ab731aad201
native_flutter_transfer_plugin: b036946c34e2b1f80c39494141a4583ab133d81a
SDWebImage: c325cf02c30337336b95beff20a13df489ec0ec9
TMCache: 95ebcc9b3c7e90fb5fd8fc3036cba3aa781c9bed
xservice_kit: a2f1b35addc126fce8687aeb183ab0c1ada5b7f3
PODFILE CHECKSUM: da43d7253de180945c825fd8c2ae13ffac2b1bfb
COCOAPODS: 1.7.0
//
// GMCache.h
// Gengmei
//
// Created by Thierry on 1/5/15.
// Copyright (c) 2015 Wanmeichuangyi. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <TMCache/TMCache.h>
@interface GMCache : NSObject
#pragma mark - 异步磁盘读写操作
/*** @brief 由对应的键获取对应的缓存数据*/
+ (void)storeObjectAtDiskWithkey:(NSString *)key
object:(id <NSCoding>)object
block:(TMDiskCacheObjectBlock)block;
/*** @brief 给特定的键,标记缓存数据并缓存*/
+ (void)fetchObjectAtDiskWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block;
/*** @brief 删除特定的键,对应的缓存数据*/
+ (void)removeObjectAtDiskWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block;
/*** @brief 清空所有的缓存数据*/
+ (void)removeAllObjectsAtDiskWithBlock:(TMDiskCacheBlock)block;
#pragma mark - 同步磁盘读写操作
/*** @brief 由对应的键获取对应的缓存数据*/
+ (void)storeObjectAtDiskWithkey:(NSString *)key
object:(id <NSCoding>)object;
/*** @brief 给特定的键,标记缓存数据并缓存*/
+ (id)fetchObjectAtDiskWithkey:(NSString *)key;
/*** @brief 删除特定的键,对应的缓存数据*/
+ (void)removeObjectAtDiskWithkey:(NSString *)key;
/*** @brief 清空所有的缓存数据*/
+ (void)removeAllObjectsAtDisk;
#pragma mark - 异步内存读写操作
/*** @brief 由对应的键获取对应的缓存数据*/
+ (void)storeObjectAtMemoryWithkey:(NSString *)key
object:(id <NSCoding>)object
block:(TMMemoryCacheObjectBlock)block;
/*** @brief 给特定的键,标记缓存数据并缓存*/
+ (void)fetchObjectAtMemoryWithkey:(NSString *)key
block:(TMMemoryCacheObjectBlock)block;
/*** @brief 删除特定的键,对应的缓存数据*/
+ (void)removeObjectAtMemoryWithkey:(NSString *)key
block:(TMMemoryCacheObjectBlock)block;
/*** @brief 清空所有的缓存数据*/
+ (void)removeAllObjectsAtMemoryWithBlock:(TMMemoryCacheBlock)block;
#pragma mark - 同步内存读写操作
/*** @brief 由对应的键获取对应的缓存数据*/
+ (void)storeObjectAtMemoryWithkey:(NSString *)key
object:(id)object;
/*** @brief 给特定的键,标记缓存数据并缓存*/
+ (id)fetchObjectAtMemoryWithkey:(NSString *)key;
/*** @brief 删除特定的键,对应的缓存数据*/
+ (void)removeObjectAtMemoryWithkey:(NSString *)key;
/*** @brief 清空所有的缓存数据*/
+ (void)removeAllObjectsAtMemory;
#pragma mark - 这里将数据缓存到Ducument目录下。异步
+ (void)storeObjectAtDocumentPathWithkey:(NSString *)key
object:(id <NSCoding>)object
block:(TMDiskCacheObjectBlock)block;
+ (void)fetchObjectAtDocumentPathWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block;
+ (void)removeObjectAtDocumentPathWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block;
+ (void)removeAllObjectsAtDocumentPathWithBlock:(TMDiskCacheBlock)block;
#pragma mark - 这里将数据缓存到Ducument目录下。同步内存读写操作
+ (void)storeObjectAtDocumentPathWithkey:(NSString *)key
object:(id <NSCoding>)object;
+ (id)fetchObjectAtDocumentPathWithkey:(NSString *)key;
+ (void)removeObjectAtDocumentPathWithkey:(NSString *)key;
+ (void)removeAllObjectsAtDocumentPath;
@end
//
// GMCache.m
// Gengmei
//
// Created by Thierry on 1/5/15.
// Copyright (c) 2015 Wanmeichuangyi. All rights reserved.
//
#import "GMCache.h"
@interface WMDocumentCache : TMCache
@end
@implementation GMCache
+ (void)storeObjectAtDiskWithkey:(NSString *)key object:(id <NSCoding>)object block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].diskCache setObject:object forKey:key block:block];
}
+ (void)fetchObjectAtDiskWithkey:(NSString *)key block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].diskCache objectForKey:key block:block];
}
+ (void)removeObjectAtDiskWithkey:(NSString *)key block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].diskCache removeObjectForKey:key block:block];
}
+ (void)removeAllObjectsAtDiskWithBlock:(TMDiskCacheBlock)block{
[[TMCache sharedCache].diskCache removeAllObjects:block];
}
+ (void)storeObjectAtDiskWithkey:(NSString *)key object:(id <NSCoding>)object{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].diskCache setObject:object forKey:key];
}
+ (id)fetchObjectAtDiskWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return nil;
}
return [[TMCache sharedCache].diskCache objectForKey:key];
}
+ (void)removeObjectAtDiskWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].diskCache removeObjectForKey:key];
}
+ (void)removeAllObjectsAtDisk{
[[TMCache sharedCache].diskCache removeAllObjects];
}
+ (void)storeObjectAtMemoryWithkey:(NSString *)key object:(id <NSCoding>)object block:(TMMemoryCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].memoryCache setObject:object forKey:key block:block];
}
+ (void)fetchObjectAtMemoryWithkey:(NSString *)key block:(TMMemoryCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].memoryCache objectForKey:key block:block];
}
+ (void)removeObjectAtMemoryWithkey:(NSString *)key block:(TMMemoryCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].memoryCache removeObjectForKey:key block:block];
}
+ (void)removeAllObjectsAtMemoryWithBlock:(TMMemoryCacheBlock)block{
[[TMCache sharedCache].memoryCache removeAllObjects:block];
}
+ (void)storeObjectAtMemoryWithkey:(NSString *)key object:(id)object{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].memoryCache setObject:object forKey:key];
}
+ (id)fetchObjectAtMemoryWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return nil;
}
return [[TMCache sharedCache].memoryCache objectForKey:key];
}
+ (void)removeObjectAtMemoryWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[TMCache sharedCache].memoryCache removeObjectForKey:key];
}
+ (void)removeAllObjectsAtMemory{
[[TMCache sharedCache].memoryCache removeAllObjects];
}
+ (void)storeObjectAtDocumentPathWithkey:(NSString *)key
object:(id <NSCoding>)object
block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[WMDocumentCache sharedCache].diskCache setObject:object forKey:key block:block];
}
+ (void)fetchObjectAtDocumentPathWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[WMDocumentCache sharedCache].diskCache objectForKey:key block:block];
}
+ (void)removeObjectAtDocumentPathWithkey:(NSString *)key
block:(TMDiskCacheObjectBlock)block{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[WMDocumentCache sharedCache].diskCache removeObjectForKey:key block:block];
}
+ (void)removeAllObjectsAtDocumentPathWithBlock:(TMDiskCacheBlock)block{
[[WMDocumentCache sharedCache].diskCache removeAllObjects:block];
}
+ (void)storeObjectAtDocumentPathWithkey:(NSString *)key
object:(id <NSCoding>)object{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[WMDocumentCache sharedCache].diskCache setObject:object forKey:key];
}
+ (id)fetchObjectAtDocumentPathWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return nil;
}
return [[WMDocumentCache sharedCache].diskCache objectForKey:key];
}
+ (void)removeObjectAtDocumentPathWithkey:(NSString *)key{
NSAssert([GMCache gmCache_isNonEmpty:key], @"key不能为空");
if (![GMCache gmCache_isNonEmpty:key]) {
return;
}
[[WMDocumentCache sharedCache].diskCache removeObjectForKey:key];
}
+ (void)removeAllObjectsAtDocumentPath{
[[WMDocumentCache sharedCache].diskCache removeAllObjects];
}
+ (BOOL)gmCache_isNonEmpty:(NSString *)key {
NSMutableCharacterSet *emptyStringSet = [[NSMutableCharacterSet alloc] init];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[emptyStringSet formUnionWithCharacterSet: [NSCharacterSet characterSetWithCharactersInString: @" "]];
if ([key length] == 0) {
return NO;
}
NSString* str = [key stringByTrimmingCharactersInSet:emptyStringSet];
return [str length] > 0;
}
@end
NSString * const WMCacheSharedName = @"WMCacheShared";
@implementation WMDocumentCache
+ (instancetype)sharedCache{
static id cache;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
cache = [[self alloc] initWithName:WMCacheSharedName rootPath:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
});
return cache;
}
@end
Copyright (c) 2016 wangyang <wangyang@wanmeizhensuo.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# GMCache
[![CI Status](http://img.shields.io/travis/wangyang/GMCache.svg?style=flat)](https://travis-ci.org/wangyang/GMCache)
[![Version](https://img.shields.io/cocoapods/v/GMCache.svg?style=flat)](http://cocoapods.org/pods/GMCache)
[![License](https://img.shields.io/cocoapods/l/GMCache.svg?style=flat)](http://cocoapods.org/pods/GMCache)
[![Platform](https://img.shields.io/cocoapods/p/GMCache.svg?style=flat)](http://cocoapods.org/pods/GMCache)
## Usage
To run the example project, clone the repo, and run `pod install` from the Example directory first.
## Requirements
## Installation
GMCache is available through [CocoaPods](http://cocoapods.org). To install
it, simply add the following line to your Podfile:
```ruby
pod "GMCache"
```
## Author
wangyang, wangyang@wanmeizhensuo.com
## License
GMCache is available under the MIT license. See the LICENSE file for more info.
Copyright (c) 2016 北京更美互动信息科技有限公司
仅限北京更美互动信息科技有限公司内部使用
//
// UIDevice+Reso.m
// Simple UIDevice Category for handling different iOSs hardware resolutions
//
// Created by Alejandro Luengo on 29/09/14.
// (c) 2014 Intelygenz
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, UIDeviceResolution) {
UnknowDevice = 0, //未知设备
iPhone35Inches = 1, //3.5英寸、iphone4s
iPhone40Inches = 2, //4英寸iphone5、iphone5c、iphone5s
iPhone47Inches = 3, //4.7英寸iphone6、iphone7、iphone8
iPhone55Inches = 4, //5.5英寸iphone6 plus、iphone7 plus、iphone8 plus
iPhone58Inches = 5, //5.5英寸iphoneX
iPhoneXRInches = 6, //iphone XR
iPhoneXSMaxInches = 7 //iphone XS Max
};
typedef NS_ENUM(NSInteger, GMDeviceType) {
GMDeviceTypeUnknow = 0,
GMDeviceTypeSimulator,
GMDeviceTypeIPad,
GMDeviceTypeIPod,
GMDeviceTypeIPhone4,
GMDeviceTypeIPhone4S,
GMDeviceTypeIPhone5,
GMDeviceTypeIPhone5S,
GMDeviceTypeIPhone5C,
GMDeviceTypeIPhone6,
GMDeviceTypeIPhone6P,
GMDeviceTypeIPhone6S,
GMDeviceTypeIPhone6SP,
GMDeviceTypeIPhone7,
GMDeviceTypeIPhone7P,
GMDeviceTypeIPhone8,
GMDeviceTypeIPhone8P,
GMDeviceTypeIPhoneX,
GMDeviceTypeIPhoneXR,
GMDeviceTypeIPhoneXS,
GMDeviceTypeIPhoneXSMax,
};
typedef NS_ENUM(NSUInteger, GMDeviceAspectRatioType) {
GMDeviceAspectRatioTypeNarrow, //iPad、iphone4s、iphone5
GMDeviceAspectRatioTypeNormal, // Normal
GMDeviceAspectRatioTypeWide, // Plus、Max
};
@interface UIDevice (Resolutions)
/**
* @author licong, 16-12-29 17:12:54
*
* 判断设备的类型(eg:当前设备是5还是6s)
*
* @return 返回设备类型
*
* @since 5.8
*/
+ (UIDeviceResolution)resolutionType __deprecated_msg("A note to the developers using it.");
+ (GMDeviceAspectRatioType)aspectRatioType;
+ (NSString *)platform;
/**
获取设备的deviceId,如果能去到idfa,就返回idfa,否则就返回idfv
@author zhaiguojun 16-10-31 in (null)
@return deviceId
@since 6.5.0
*/
+ (NSString *)deviceId;
/**
获取设备详细类型,目前只到6s,和6s plus
@author zhaiguojun 16-09-18 in (null)
@return model
@since 6.3.0
*/
+ (NSString*)deviceVersion;
@end
//
// UIDevice+Reso.m
// Simple UIDevice Category for handling different iOSs hardware resolutions
//
// Created by Alejandro Luengo on 29/09/14.
// (c) 2014 Intelygenz
// iPhone 6 Plus 736x414 points 2208x1242 pixels 3x scale
// iPhone 6 667x375 points 1334x750 pixels 2x scale
// iPhone 5 568x320 points 1136x640 pixels 2x scale
// iPhone 4s 480x320 points 960x640 pixels 2x scale
// iPad 1024x768 points 1024x768 pixels 1x scale
// iPad Retina 1024x768 points 2048x1536 pixels 2x scale
#import "UIDevice+Resolutions.h"
#import "sys/utsname.h"
#import <AdSupport/AdSupport.h>
@implementation UIDevice (Resolutions)
+ (NSString *)deviceId {
NSString *idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
NSString *idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
if (![idfa isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
return idfa;
}
return idfv;
}
+ (UIDeviceResolution)resolutionType
{
UIScreenMode *mode = [UIScreen mainScreen].preferredMode;
if (mode.size.height == 960) {
return iPhone35Inches;
}
else if (mode.size.height == 1136) {
return iPhone40Inches;
}
else if (mode.size.height == 1334) {
return iPhone47Inches;
}
else if (mode.size.height == 2208) {
return iPhone55Inches;
}
else if (mode.size.height == 2436) {
return iPhone58Inches;
}
else if (mode.size.height == 1792) {
return iPhoneXRInches;
}
else if (mode.size.height == 2688) {
return iPhoneXSMaxInches;
}
else
return UnknowDevice;
}
+ (GMDeviceAspectRatioType)aspectRatioType {
UIDevice *device = [UIDevice currentDevice];
if ([device.model containsString:@"iPad"])
return GMDeviceAspectRatioTypeNarrow;
NSString *deviceVersion = [self deviceVersion];
UIScreenMode *mode = [UIScreen mainScreen].preferredMode;
if ([deviceVersion containsString:@"iPhone 4"] || [deviceVersion containsString:@"iPhone 5"] || mode.size.height == 960 || mode.size.height == 1136)
return GMDeviceAspectRatioTypeNarrow;
else if ([deviceVersion containsString:@"Plus"] || [deviceVersion containsString:@"Max"])
return GMDeviceAspectRatioTypeWide;
else
return GMDeviceAspectRatioTypeNormal;
}
+ (NSString *)platform {
struct utsname systemInfo;
uname(&systemInfo);
NSString *platform = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
return platform;
}
+ (NSString*)deviceVersion{
NSString *platform = [self platform];
//iPhone
if ([platform isEqualToString:@"iPhone3,1"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone3,2"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone3,3"]) return @"iPhone 4";
if ([platform isEqualToString:@"iPhone4,1"]) return @"iPhone 4S";
if ([platform isEqualToString:@"iPhone5,1"]) return @"iPhone 5";
if ([platform isEqualToString:@"iPhone5,2"]) return @"iPhone 5";
if ([platform isEqualToString:@"iPhone5,3"]) return @"iPhone 5C";
if ([platform isEqualToString:@"iPhone5,4"]) return @"iPhone 5C";
if ([platform isEqualToString:@"iPhone6,1"]) return @"iPhone 5S";
if ([platform isEqualToString:@"iPhone6,2"]) return @"iPhone 5S";
if ([platform isEqualToString:@"iPhone7,1"]) return @"iPhone 6 Plus";
if ([platform isEqualToString:@"iPhone7,2"]) return @"iPhone 6";
if ([platform isEqualToString:@"iPhone8,1"]) return @"iPhone 6s";
if ([platform isEqualToString:@"iPhone8,2"]) return @"iPhone 6s Plus";
if ([platform isEqualToString:@"iPhone8,3"]) return @"iPhoneSE";
if ([platform isEqualToString:@"iPhone8,4"]) return @"iPhoneSE";
if ([platform isEqualToString:@"iPhone9,1"]) return @"iPhone 7";
if ([platform isEqualToString:@"iPhone9,3"]) return @"iPhone 7";
if ([platform isEqualToString:@"iPhone9,2"]) return @"iPhone 7 Plus";
if ([platform isEqualToString:@"iPhone9,4"]) return @"iPhone 7 Plus";
if ([platform isEqualToString:@"iPhone10,1"]) return @"iPhone 8";
if ([platform isEqualToString:@"iPhone10,4"]) return @"iPhone 8";
if ([platform isEqualToString:@"iPhone10,2"]) return @"iPhone 8 Plus";
if ([platform isEqualToString:@"iPhone10,5"]) return @"iPhone 8 Plus";
if ([platform isEqualToString:@"iPhone10,3"]) return @"iPhone X";
if ([platform isEqualToString:@"iPhone10,6"]) return @"iPhone X";
if ([platform isEqualToString:@"iPhone11,8"]) return @"iPhone XR";
if ([platform isEqualToString:@"iPhone11,2"]) return @"iPhone XS";
if ([platform isEqualToString:@"iPhone11,4"]) return @"iPhone XS Max";
if ([platform isEqualToString:@"iPhone11,6"]) return @"iPhone XS Max";
return @"iphone X ++";
}
@end
//
// UIImage+GM.h
// Gengmei
//
// Created by licong on 15/12/29. Rewrite by wangyang on 2016-7-4
// Copyright © 2016年 Wanmeichuangyi. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIImage (GM)
/**
* @brief 通过颜色生成图片
*
* @param color 颜色
*/
+ (UIImage *)imageWithColor:(UIColor *)color;
/**
* @author licong, 16-12-29 18:12:48
*
* 将图片调整到设定大小
*
* @param size 设定的图片大小
*
* @return 得到设定大小后的图片
*
* @since 5.8
*/
- (UIImage *)resizedImageWithSize:(CGSize)size;
/**
使用该方法的前提条件是认为这个图片为3倍图,使用该方法将返回一个适用于当前设备scale的UIImage实例。
比如一张90像素x90像素的网络下载的图片,使用该方法
- iPhone 8 Plus机型将返回一个size为(30, 30),scale=3的UIImage;
- iPhone 8 机型将返回返回一个size为(30, 30),scale=2的UIImage;
*/
- (UIImage *)resizeToDeviceScale;
/**
* @author wangyang in 2016-7-1
*
* 参考rotatedWithTransform
*/
- (UIImage *)rotatedWithAngle:(double)angle;
/**
* @author wangyang in 2016-7-1
*
* 使用这个方法旋转图片,导出的图片的imageOrientation为Up,绘制时会同时考虑待旋转图片的原 imageOrientation 与参数 transform
@note
1. 使用这个方法进行旋转,导出的图片大小可能会发生变化。比如旋转45度,那么能容下原图的bounds肯定要大一些;比如旋转90度,图片的高宽就会互换。
2. 旋转角度为0时,可以用来将图片的imageOrientation重置为Up。
* @return 新图片
*/
- (UIImage *)rotatedWithTransform:(CGAffineTransform)transform;
/**
* @author wangyang in 2016-7-4
*
* 裁剪图片
*/
- (UIImage *)imageWithCropRect:(CGRect)rect;
@end
//
// UIImage+GM.m
// Gengmei
//
// Created by licong on 15/12/29. Rewrite by wangyang on 2016-7-4
// Copyright © 2016年 Wanmeichuangyi. All rights reserved.
//
#import "UIImage+GM.h"
@implementation UIImage (GM)
#pragma mark - 从颜色生成图片
+ (UIImage *)imageWithColor:(UIColor *)color {
CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
UIGraphicsBeginImageContext(rect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, rect);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
#pragma mark - 改变大小
- (UIImage *)resizedImageWithSize:(CGSize)size {
UIGraphicsBeginImageContext(size);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
// CGContextDrawImage时需要额外考虑UIImage.imageOrientation,drawInRect可以自动帮我们做好这件事
[self drawInRect:rect];
// 另外,我们不需要指定使用 CGContextSetInterpolationQuality,使用默认值Hight就可以。参考http://stackoverflow.com/questions/5685884/imagequality-with-cgcontextsetinterpolationquality
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
- (UIImage *)resizeToDeviceScale {
CGSize newSize = CGSizeMake(ceilf(self.size.width / 3), ceilf(self.size.height / 3));
UIGraphicsBeginImageContextWithOptions(newSize, NO, [UIScreen mainScreen].scale);
CGRect rect = CGRectMake(0, 0, newSize.width, newSize.height);
// CGContextDrawImage时需要额外考虑UIImage.imageOrientation,drawInRect可以自动帮我们做好这件事
[self drawInRect:rect];
// 另外,我们不需要指定使用 CGContextSetInterpolationQuality,使用默认值Hight就可以。参考http://stackoverflow.com/questions/5685884/imagequality-with-cgcontextsetinterpolationquality
UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImg;
}
#pragma mark - 旋转
- (UIImage *)rotatedWithAngle:(double)angle
{
CGAffineTransform transform = CGAffineTransformMakeRotation(angle * M_PI / 180);
return [self rotatedWithTransform:transform];
}
- (UIImage *)rotatedWithTransform:(CGAffineTransform)transform {
// 旋转后图片的大小
CGRect rotatedRect = CGRectApplyAffineTransform(CGRectMake(0, 0, self.size.width, self.size.height), transform);
rotatedRect.origin.x = 0;
rotatedRect.origin.y = 0;
CGSize rotatedSize = rotatedRect.size;
UIGraphicsBeginImageContextWithOptions(rotatedSize, YES, self.scale);
CGContextRef bitmap = UIGraphicsGetCurrentContext();
// 将 origin 图片的移动中间, 这样我们就可以绕着中心点旋转
CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2);
// 旋转画布布
CGContextConcatCTM(bitmap, transform);
// 再将 origin 移回来
CGContextTranslateCTM(bitmap, rotatedSize.width / -2, rotatedSize.height / -2);
// drawInRect 会考虑图片的 orientation,也会考虑当前Context的变换
[self drawInRect:rotatedRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
#pragma mark - 剪切
- (UIImage *)imageWithCropRect:(CGRect)rect
{
CGImageRef croppedImage = CGImageCreateWithImageInRect(self.CGImage, rect);
UIImage *image = [UIImage imageWithCGImage:croppedImage];
CGImageRelease(croppedImage);
return image;
}
@end
//
// UILabel+CopyExtern.h
// CopyLabel
//
// Created by XingBo on 14-4-1.
// Copyright (c) 2014年 XingBo. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UILabel (CopyExtern)
- (void)attachTapHandler;
@end
//
// UILabel+CopyExtern.m
// CopyLabel
//
// Created by XingBo on 14-4-1.
// Copyright (c) 2014年 XingBo. All rights reserved.
//
#import "UILabel+CopyExtern.h"
@implementation UILabel (CopyExtern)
-(BOOL)canBecomeFirstResponder
{
return YES;
}
// 可以响应的方法
-(BOOL)canPerformAction:(SEL)action withSender:(id)sender
{
return (action == @selector(copyText:));
}
//针对于响应方法的实现
-(void)copyText:(id)sender
{
UIPasteboard *pboard = [UIPasteboard generalPasteboard];
/**
fix帖子详情页 楼主发帖复制崩溃bug
因为某些富文本label只设置了attributedText,导致self.text为nil崩溃
*/
if (self.text) {
pboard.string = self.text;
}else{
pboard.string = self.attributedText.string;
}
}
//UILabel默认是不接收事件的,我们需要自己添加touch事件
-(void)attachTapHandler{
self.userInteractionEnabled = YES; //用户交互的总开关
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
[self addGestureRecognizer:longPress];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillHideMenuNotification object:nil];
}
#pragma mark - notifation methods
- (void)menuShow:(NSNotification *)sender
{
if (self.isFirstResponder) {
self.backgroundColor = [UIColor grayColor];
}
}
- (void)menuHide:(NSNotification *)sender
{
self.backgroundColor = [UIColor clearColor];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void)handleTap:(UIGestureRecognizer*) recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
[self becomeFirstResponder];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuShow:) name:UIMenuControllerWillShowMenuNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuHide:) name:UIMenuControllerWillHideMenuNotification object:nil];
[[UIMenuController sharedMenuController] setMenuItems:nil];
UIMenuItem *copyLink = [[UIMenuItem alloc] initWithTitle:@"复制"
action:@selector(copyText:)];
[[UIMenuController sharedMenuController] setMenuItems:[NSArray arrayWithObjects:copyLink, nil]];
[[UIMenuController sharedMenuController] setTargetRect:self.frame inView:self.superview];
[[UIMenuController sharedMenuController] setMenuVisible:YES animated: YES];
// label复制选中时的背景颜色
self.backgroundColor = [UIColor clearColor];
}
}
@end
//
// UITextView+Keyboard.h
// ZhengXing
//
// Created by Tulipa on 14-8-22.
// Copyright (c) 2014年 Wanmei Creative. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UITextView (Keyboard)
@property (nonatomic, assign) BOOL hasCloseButton;
@end
@interface UITextField (Keyboard)
@property (nonatomic, assign) BOOL hasCloseButton;
@end
//
// UITextView+Keyboard.m
// ZhengXing
//
// Created by Tulipa on 14-8-22.
// Copyright (c) 2014年 Wanmei Creative. All rights reserved.
//
#import "UITextView+Keyboard.h"
#import "UIView+Layout.h"
@implementation UITextView (Keyboard)
- (BOOL)hasCloseButton
{
return self.inputAccessoryView != nil;
}
- (void)setHasCloseButton:(BOOL)hasCloseButton
{
if (hasCloseButton)
{
UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[closeButton setBackgroundImage:[UIImage imageNamed:@"keyboard"] forState:UIControlStateNormal];
[closeButton sizeToFit];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, closeButton.height)];
[view addSubview:closeButton];
[view setBackgroundColor:[UIColor clearColor]];
[closeButton setRight:view.width];
[closeButton setTop:0];
[closeButton addTarget:self action:@selector(resignFirstResponder) forControlEvents:UIControlEventTouchUpInside];
self.inputAccessoryView = view;
}
else
{
self.inputAccessoryView = nil;
}
}
@end
@implementation UITextField (Keyboard)
- (BOOL)hasCloseButton
{
return self.inputAccessoryView != nil;
}
- (void)setHasCloseButton:(BOOL)hasCloseButton
{
if (hasCloseButton)
{
UIButton *closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
[closeButton setBackgroundImage:[UIImage imageNamed:@"keyboard"] forState:UIControlStateNormal];
[closeButton sizeToFit];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [[UIScreen mainScreen] bounds].size.width, closeButton.height)];
[view addSubview:closeButton];
[view setBackgroundColor:[UIColor clearColor]];
[closeButton setRight:view.width];
[closeButton setTop:0];
[closeButton addTarget:self action:@selector(resignFirstResponder) forControlEvents:UIControlEventTouchUpInside];
self.inputAccessoryView = view;
}
else
{
self.inputAccessoryView = nil;
}
}
@end
//
// UIView+Layout.h
// Gengmei
//
// Created by licong on 15/12/29.
// Copyright © 2015年 Wanmeichuangyi. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (Layout)
/**
* Shortcut for frame.origin.x.
*
* Sets frame.origin.x = left
*/
@property (nonatomic) CGFloat left;
/**
* Shortcut for frame.origin.y
*
* Sets frame.origin.y = top
*/
@property (nonatomic) CGFloat top;
/**
* Shortcut for frame.origin.x + frame.size.width
*
* Sets frame.origin.x = right - frame.size.width
*/
@property (nonatomic) CGFloat right;
/**
* Shortcut for frame.origin.y + frame.size.height
*
* Sets frame.origin.y = bottom - frame.size.height
*/
@property (nonatomic) CGFloat bottom;
/**
* Shortcut for frame.size.width
*
* Sets frame.size.width = width
*/
@property (nonatomic) CGFloat width;
/**
* Shortcut for frame.size.height
*
* Sets frame.size.height = height
*/
@property (nonatomic) CGFloat height;
/**
* Shortcut for center.x
*
* Sets center.x = centerX
*/
@property (nonatomic) CGFloat centerX;
/**
* Shortcut for center.y
*
* Sets center.y = centerY
*/
@property (nonatomic) CGFloat centerY;
/**
* Return the x coordinate on the screen.
*/
@property (nonatomic, readonly) CGFloat screenX;
/**
* Return the y coordinate on the screen.
*/
@property (nonatomic, readonly) CGFloat screenY;
/**
* Return the x coordinate on the screen, taking into account scroll views.
*/
@property (nonatomic, readonly) CGFloat screenViewX;
/**
* Return the y coordinate on the screen, taking into account scroll views.
*/
@property (nonatomic, readonly) CGFloat screenViewY;
/**
* Return the view frame on the screen, taking into account scroll views.
*/
@property (nonatomic, readonly) CGRect screenFrame;
/**
* Shortcut for frame.origin
*/
@property (nonatomic) CGPoint origin;
/**
* Shortcut for frame.size
*/
@property (nonatomic) CGSize size;
/**
* Return the width in portrait or the height in landscape.
*/
@property (nonatomic, readonly) CGFloat orientationWidth;
/**
* Return the height in portrait or the width in landscape.
*/
@property (nonatomic, readonly) CGFloat orientationHeight;
/**
* Finds the first descendant view (including this view) that is a member of a particular class.
*/
- (UIView*)descendantOrSelfWithClass:(Class)cls;
/**
* Finds the first ancestor view (including this view) that is a member of a particular class.
*/
- (UIView*)ancestorOrSelfWithClass:(Class)cls;
/**
* Removes all subviews.
*/
- (void)removeAllSubviews;
@end
@interface UIView (GestureAction)
/**
Attaches the given block for a single tap action to the receiver.
@param block The block to execute.
*/
- (void)setTapActionWithBlock:(void (^)(void))block;
//- (void)removeTapAction;
/**
Attaches the given block for a long press action to the receiver.
@param block The block to execute.
*/
- (void)setLongPressActionWithBlock:(void (^)(void))block;
@end
//
// UIView+Layout.m
// Gengmei
//
// Created by licong on 15/12/29.
// Copyright © 2015年 Wanmeichuangyi. All rights reserved.
//
#import "UIView+Layout.h"
#import <objc/runtime.h>
static char kDTActionHandlerTapBlockKey;
static char kDTActionHandlerTapGestureKey;
static char kDTActionHandlerLongPressBlockKey;
static char kDTActionHandlerLongPressGestureKey;
@implementation UIView (Layout)
- (CGFloat)left {
return self.frame.origin.x;
}
- (void)setLeft:(CGFloat)x {
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)top {
return self.frame.origin.y;
}
- (void)setTop:(CGFloat)y {
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)right {
return self.frame.origin.x + self.frame.size.width;
}
- (void)setRight:(CGFloat)right {
CGRect frame = self.frame;
frame.origin.x = right - frame.size.width;
self.frame = frame;
}
- (CGFloat)bottom {
return self.frame.origin.y + self.frame.size.height;
}
- (void)setBottom:(CGFloat)bottom {
CGRect frame = self.frame;
frame.origin.y = bottom - frame.size.height;
self.frame = frame;
}
- (CGFloat)centerX {
return self.center.x;
}
- (void)setCenterX:(CGFloat)centerX {
self.center = CGPointMake(centerX, self.center.y);
}
- (CGFloat)centerY {
return self.center.y;
}
- (void)setCenterY:(CGFloat)centerY {
self.center = CGPointMake(self.center.x, centerY);
}
- (CGFloat)width {
return self.frame.size.width;
}
- (void)setWidth:(CGFloat)width {
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)height {
return self.frame.size.height;
}
- (void)setHeight:(CGFloat)height {
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)screenX {
CGFloat x = 0.0f;
for (UIView* view = self; view; view = view.superview) {
x += view.left;
}
return x;
}
- (CGFloat)screenY {
CGFloat y = 0.0f;
for (UIView* view = self; view; view = view.superview) {
y += view.top;
}
return y;
}
- (CGFloat)screenViewX {
CGFloat x = 0.0f;
for (UIView* view = self; view; view = view.superview) {
x += view.left;
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView* scrollView = (UIScrollView*)view;
x -= scrollView.contentOffset.x;
}
}
return x;
}
- (CGFloat)screenViewY {
CGFloat y = 0;
for (UIView* view = self; view; view = view.superview) {
y += view.top;
if ([view isKindOfClass:[UIScrollView class]]) {
UIScrollView* scrollView = (UIScrollView*)view;
y -= scrollView.contentOffset.y;
}
}
return y;
}
- (CGRect)screenFrame {
return CGRectMake(self.screenViewX, self.screenViewY, self.width, self.height);
}
- (CGPoint)origin {
return self.frame.origin;
}
- (void)setOrigin:(CGPoint)origin {
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGSize)size {
return self.frame.size;
}
- (void)setSize:(CGSize)size {
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGFloat)orientationWidth {
return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)
? self.height : self.width;
}
- (CGFloat)orientationHeight {
return UIInterfaceOrientationIsLandscape([UIApplication sharedApplication].statusBarOrientation)
? self.width : self.height;
}
- (UIView*)descendantOrSelfWithClass:(Class)cls {
if ([self isKindOfClass:cls])
return self;
for (UIView* child in self.subviews) {
UIView* it = [child descendantOrSelfWithClass:cls];
if (it)
return it;
}
return nil;
}
- (UIView*)ancestorOrSelfWithClass:(Class)cls {
if ([self isKindOfClass:cls]) {
return self;
} else if (self.superview) {
return [self.superview ancestorOrSelfWithClass:cls];
} else {
return nil;
}
}
- (void)removeAllSubviews {
while (self.subviews.count) {
UIView* child = self.subviews.lastObject;
[child removeFromSuperview];
}
}
- (CGPoint)offsetFromView:(UIView*)otherView {
CGFloat x = 0.0f, y = 0.0f;
for (UIView* view = self; view && view != otherView; view = view.superview) {
x += view.left;
y += view.top;
}
return CGPointMake(x, y);
}
@end
@implementation UIView (GestureAction)
- (void)setTapActionWithBlock:(void (^)(void))block
{
UITapGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerTapGestureKey);
if (!gesture)
{
gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForTapGesture:)];
[self addGestureRecognizer:gesture];
objc_setAssociatedObject(self, &kDTActionHandlerTapGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &kDTActionHandlerTapBlockKey, block, OBJC_ASSOCIATION_COPY);
}
- (void)__handleActionForTapGesture:(UITapGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateRecognized)
{
void(^action)(void) = objc_getAssociatedObject(self, &kDTActionHandlerTapBlockKey);
if (action)
{
action();
}
}
}
- (void)setLongPressActionWithBlock:(void (^)(void))block
{
UILongPressGestureRecognizer *gesture = objc_getAssociatedObject(self, &kDTActionHandlerLongPressGestureKey);
if (!gesture)
{
gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(__handleActionForLongPressGesture:)];
[self addGestureRecognizer:gesture];
objc_setAssociatedObject(self, &kDTActionHandlerLongPressGestureKey, gesture, OBJC_ASSOCIATION_RETAIN);
}
objc_setAssociatedObject(self, &kDTActionHandlerLongPressBlockKey, block, OBJC_ASSOCIATION_COPY);
}
- (void)__handleActionForLongPressGesture:(UITapGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan)
{
void(^action)(void) = objc_getAssociatedObject(self, &kDTActionHandlerLongPressBlockKey);
if (action)
{
action();
}
}
}
@end
//
// UIView+LineWithAutolayout.h
// ZhengXing
//
// Created by wangyang on 11/6/14.
// Copyright (c) 2014 Wanmei Creative. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (LineWithAutolayout)
/**
* @author licong, 16-12-28 19:12:48
*
* alloc一个无固定大小的view,用作分割线
*
* @return 返回一个view
*
* @since 5.8
*/
- (UIView *)addLineWithoutFrame;
/**
* @author licong, 16-12-28 19:12:16
*
* 在指定view(就是self)的最上边缘,加一根与指定view宽度相等的头部边缘线
*
* @return 返回alloc的分割线
*
* @since 5.8
*/
- (UIView *)addTopLine;
/**
* @author licong, 16-12-28 19:12:16
*
* 在view(就是self)的最下边缘,加一根与指定view宽度相等的底部边缘线
*
* @return 返回alloc的分割线
*
* @since 5.8
*/
- (UIView *)addBottomLine;
/**
* @author licong, 16-12-28 19:13:30
*
* 给指定的view(就是self),加一跟中心水平线
*
* @param left 竖直线距离设定的view左边的距离
* @param right 竖直线距离设定的view右边的距离
*
* @return 返回alloc的中心竖直线
*
* @since 5.8
*/
- (UIView *)addCenterHorizontalLineWithLeft:(CGFloat)left right:(CGFloat)right;
/**
* @author licong, 16-12-28 19:12:40
*
* 给指定的view(就是self),加一跟中心竖直线
*
* @param top 竖直线距离设定的view上边的距离
* @param bottom 竖直线距离设定的view下边的距离
*
* @return 返回alloc的中心竖直线
*
* @since 5.8
*/
- (UIView *)addCenterVerticalLineWithTop:(CGFloat)top bottom:(CGFloat)bottom;
/**
* @author licong, 16-12-28 19:12:19
*
* 给指定的view(就是self)的最下边缘,加一根长度可设置的头部边缘线(最长不超过该view的宽度)
*
* @param left 头部边缘线距离设定view左边的距离
* @param right 头部边缘线距离设定view左边的距离
*
* @return 返回长度可设置的头部边缘线
*
* @since 5.8
*/
- (UIView *)addTopLineWithLeft:(CGFloat)left right:(CGFloat)right;
/**
* @author licong, 16-12-28 19:12:16
*
* 给指定的view(就是self)的最下边缘,加一根长度可设置的底部边缘线(最长不超过该view的宽度)
*
* @param left 底部边缘线距离设定view左边缘的距离
* @param right 底部边缘线距离设定view右边缘的距离
*
* @return 返回长度可设置的底部边缘线
*
* @since 5.8
*/
- (UIView *)addBottomLineWithLeft:(CGFloat)left right:(CGFloat)right;
/**
* @author licong, 16-12-29 11:12:02
*
* 给指定的view(就是self),加一根长度可设置的水平线,以Top为Y方向的基准点
*
* @param top 水平线距离设定view上边缘的距离
* @param left 水平线距离设定view左边缘的距离
* @param right 水平线距离设定view右边缘的距离
*
* @return 返回水平线
*
* @since 5.8
*/
- (UIView *)addHorizontalLineWithTop:(CGFloat)top left:(CGFloat)left right:(CGFloat)right;
/**
* @author licong, 16-12-29 11:12:48
*
* 给指定的view(就是self),加一根长度可设置的水平线,以Bottom为Y方向的基准点
*
* @param bottom 水平线距离设定view下边缘的距离
* @param left 水平线距离设定view左边缘的距离
* @param right 水平线距离设定view右边缘的距离
*
* @return 返回水平线
*
* @since 5.8
*/
- (UIView *)addHorizontalLineWithBottom:(CGFloat)bottom left:(CGFloat)left right:(CGFloat)right;
/**
* @author licong, 16-12-29 11:12:48
*
* 给指定的view(就是self),加一根长度可设置的竖直线,以Left为X方向的基准点
*
* @param left 竖直线距离设定view左边缘的距离
* @param top 竖直线距离设定view上边缘的距离
* @param bottom 竖直线距离设定view下边缘的距离
*
* @return 返回竖直线
*
* @since 5.8
*/
- (UIView *)addVerticalLineWithLeft:(CGFloat)left top:(CGFloat)top bottom:(CGFloat)bottom;
/**
* @author licong, 16-12-29 11:12:54
*
* 给指定的view(就是self),加一根长度可设置的竖直线,以Right为X方向的基准点
*
* @param right 竖直线距离设定view右边缘的距离
* @param top 竖直线距离设定view上边缘的距离
* @param bottom 竖直线距离设定view下边缘的距离
*
* @return 返回竖直线
*
* @since 58
*/
- (UIView *)addVerticalLineWithRight:(CGFloat)right top:(CGFloat)top bottom:(CGFloat)bottom;
@end
//
// UIView+LineWithAutolayout.m
// ZhengXing
//
// Created by wangyang on 11/6/14.
// Copyright (c) 2014 Wanmei Creative. All rights reserved.
//
#import "UIView+LineWithAutolayout.h"
#import <Masonry/Masonry.h>
#import <UIColor+GMTheme.h>
@interface OnePixelLine : UIView
@end
@implementation OnePixelLine
@end
@implementation UIView (LineWithAutolayout)
- (UIView *)addLineWithoutFrame{
UIView *line = [OnePixelLine new];
line.backgroundColor = UIColor.separatorLine;
[self addSubview:line];
return line;
}
- (UIView *)addTopLine
{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(0);
make.left.mas_equalTo(0);
make.right.mas_equalTo(0);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addBottomLine
{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(0);
make.left.mas_equalTo(0);
make.right.mas_equalTo(0);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addCenterHorizontalLineWithLeft:(CGFloat)left right:(CGFloat)right{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.mas_equalTo(0);
make.left.mas_equalTo(left);
make.right.mas_equalTo(right);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addCenterVerticalLineWithTop:(CGFloat)top bottom:(CGFloat)bottom{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(top);
make.centerX.mas_equalTo(0);
make.bottom.mas_equalTo(bottom);
make.width.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addTopLineWithLeft:(CGFloat)left right:(CGFloat)right{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(0);
make.left.mas_equalTo(left);
make.right.mas_equalTo(right);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addBottomLineWithLeft:(CGFloat)left right:(CGFloat)right{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(0);
make.left.mas_equalTo(left);
make.right.mas_equalTo(right);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addHorizontalLineWithTop:(CGFloat)top left:(CGFloat)left right:(CGFloat)right{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(top);
make.left.mas_equalTo(left);
make.right.mas_equalTo(right);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addHorizontalLineWithBottom:(CGFloat)bottom left:(CGFloat)left right:(CGFloat)right{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.bottom.mas_equalTo(bottom);
make.left.mas_equalTo(left);
make.right.mas_equalTo(right);
make.height.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addVerticalLineWithLeft:(CGFloat)left top:(CGFloat)top bottom:(CGFloat)bottom{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(top);
make.left.mas_equalTo(left);
make.bottom.mas_equalTo(bottom);
make.width.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
- (UIView *)addVerticalLineWithRight:(CGFloat)right top:(CGFloat)top bottom:(CGFloat)bottom{
UIView *line = [self addLineWithoutFrame];
[line mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(top);
make.right.mas_equalTo(right);
make.bottom.mas_equalTo(bottom);
make.width.offset(1/[UIScreen mainScreen].scale);
}];
return line;
}
@end
//
// UIViewController+ChildSwitch.h
// segmentSwitch
//
// Created by wangyang on 5/13/15.
// Copyright (c) 2015 IGIU. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIViewController (ChildControllerSwitch)
#pragma mark - 只读属性
/**
当前显示的controller
*/
@property (nonatomic, strong) UIViewController *currentController;
/**
将childContainer添加到视图中用来显示所有的child
注意:childContainer只有一个子视图:childScrollView,childScrollView装载了所有的child。
*/
@property (nonatomic, strong) IBOutlet UIView *childContainer;
/**
用来横滑切换chile controller。可以使用childScrollView.scrollEnable来禁止。
其contentSize的高为childContainer.height
*/
@property (nonatomic, strong) UIScrollView *childScrollView;
/**
返回该类别所管理的chile view controllers
*/
@property (nonatomic, copy) NSArray *childs;
#pragma mark - 需要配置的属性
/**
childContainer的size,需要手动计算出来
*/
@property (nonatomic, assign) CGSize childContainerSize;
/**
滑动childScrollView,滑动停止,调用该block。典型使用就是通知segment切换到指定位置
*/
@property (nonatomic, copy) void (^childScrollViewDidEndScroll) (NSInteger index);
#pragma mark - 可选属性
/**
switchToChildControllerAtIndex后会调用
*/
@property (nonatomic, copy) void (^didFinishSwitchChild) (void);
#pragma mark - 方法
/**
* @brief 设置第一个要显示的controller
*
*/
- (void)setFirstController:(UIViewController *)controller;
/**
* @brief 添加待显示的controllers
*
*/
- (void)addOtherController:(NSArray *)otherControllers;
/**
设置第一个需要显示的controller及其它待显示的controllers
*/
- (void)setFirstController:(UIViewController *)controller otherController:(NSArray *)otherControllers;
/**
切换到指定位置的controller
*/
- (void)switchToChildControllerAtIndex:(NSUInteger)index;
@end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
//
// GMAnnotation.h
// Gengmei
//
// Created by Thierry on 4/25/15.
// Copyright (c) 2015 Wanmeichuangyi. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
@interface GMAnnotation : NSObject<MKAnnotation>
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *subtitle;
-(id) initWithCoordinate:(CLLocationCoordinate2D) coords;
@end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment