diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index a977fd3da49..85793f127d2 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -288,6 +288,10 @@ void _OS::set_window_size(const Size2 &p_size) {
 	OS::get_singleton()->set_window_size(p_size);
 }
 
+Rect2 _OS::get_window_safe_area() const {
+	return OS::get_singleton()->get_window_safe_area();
+}
+
 void _OS::set_window_fullscreen(bool p_enabled) {
 	OS::get_singleton()->set_window_fullscreen(p_enabled);
 }
@@ -1046,6 +1050,7 @@ void _OS::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_window_position", "position"), &_OS::set_window_position);
 	ClassDB::bind_method(D_METHOD("get_window_size"), &_OS::get_window_size);
 	ClassDB::bind_method(D_METHOD("set_window_size", "size"), &_OS::set_window_size);
+	ClassDB::bind_method(D_METHOD("get_window_safe_area"), &_OS::get_window_safe_area);
 	ClassDB::bind_method(D_METHOD("set_window_fullscreen", "enabled"), &_OS::set_window_fullscreen);
 	ClassDB::bind_method(D_METHOD("is_window_fullscreen"), &_OS::is_window_fullscreen);
 	ClassDB::bind_method(D_METHOD("set_window_resizable", "enabled"), &_OS::set_window_resizable);
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index c40abd51f6d..1790c687570 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -162,6 +162,7 @@ public:
 	virtual void set_window_position(const Point2 &p_position);
 	virtual Size2 get_window_size() const;
 	virtual Size2 get_real_window_size() const;
+	virtual Rect2 get_window_safe_area() const;
 	virtual void set_window_size(const Size2 &p_size);
 	virtual void set_window_fullscreen(bool p_enabled);
 	virtual bool is_window_fullscreen() const;
diff --git a/core/os/os.h b/core/os/os.h
index 5a2c9987821..943c0498f16 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -205,6 +205,18 @@ public:
 	virtual void request_attention() {}
 	virtual void center_window();
 
+	// Returns window area free of hardware controls and other obstacles.
+	// The application should use this to determine where to place UI elements.
+	//
+	// Keep in mind the area returned is in window coordinates rather than
+	// viewport coordinates - you should perform the conversion on your own.
+	//
+	// The maximum size of the area is Rect2(0, 0, window_size.width, window_size.height).
+	virtual Rect2 get_window_safe_area() const {
+		Size2 window_size = get_window_size();
+		return Rect2(0, 0, window_size.width, window_size.height);
+	}
+
 	virtual void set_borderless_window(bool p_borderless) {}
 	virtual bool get_borderless_window() { return 0; }
 
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index ab15e35f63c..a9a40c05082 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -22,14 +22,6 @@
 		1FF4C1851F584E3F00A41E41 /* GameKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1841F584E3F00A41E41 /* GameKit.framework */; };
 		1FF4C1871F584E5600A41E41 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1861F584E5600A41E41 /* StoreKit.framework */; };
 		1FF4C1871F584E7600A41E41 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1FF4C1881F584E7600A41E41 /* StoreKit.framework */; };
-		D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */; };
-		D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */; };
-		D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */; };
-		D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */; };
-		D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */; };
-		D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */; };
-		D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */; };
-		D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */; };
 		D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D07CD44D1C5D589C00B7FB28 /* Images.xcassets */; };
 		D0BCFE3818AEBDA2004A7AAE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */; };
 		D0BCFE3A18AEBDA2004A7AAE /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0BCFE3918AEBDA2004A7AAE /* CoreGraphics.framework */; };
@@ -57,14 +49,6 @@
 		1FF4C1881F584E7600A41E41 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 		1FF4C1881F584E6300A41E41 /* $binary.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = $binary.entitlements; sourceTree = "<group>"; };
 		1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = dummy.cpp; sourceTree = "<group>"; };
-		D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
-		D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-667h@2x.png"; sourceTree = "<group>"; };
-		D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-736h@3x.png"; sourceTree = "<group>"; };
-		D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-736h@3x.png"; sourceTree = "<group>"; };
-		D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = "<group>"; };
-		D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape-1366h@2x.png"; sourceTree = "<group>"; };
-		D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = "<group>"; };
-		D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait-1366h@2x.png"; sourceTree = "<group>"; };
 		D07CD44D1C5D589C00B7FB28 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
 		D0BCFE3418AEBDA2004A7AAE /* $binary.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = $binary.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		D0BCFE3718AEBDA2004A7AAE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -159,14 +143,6 @@
 			isa = PBXGroup;
 			children = (
 				1FF4C1881F584E6300A41E41 /* $binary.entitlements */,
-				D07CD4331C5D573600B7FB28 /* Default-568h@2x.png */,
-				D07CD4351C5D573600B7FB28 /* Default-667h@2x.png */,
-				D07CD4361C5D573600B7FB28 /* Default-Portrait-736h@3x.png */,
-				D07CD4381C5D573600B7FB28 /* Default-Landscape-736h@3x.png */,
-				D07CD4391C5D573600B7FB28 /* Default-Landscape@2x.png */,
-				D07CD43A1C5D573600B7FB28 /* Default-Landscape-1366h@2x.png */,
-				D07CD43B1C5D573600B7FB28 /* Default-Portrait@2x.png */,
-				D07CD43C1C5D573600B7FB28 /* Default-Portrait-1366h@2x.png */,
 				D07CD44D1C5D589C00B7FB28 /* Images.xcassets */,
 				D0BCFE4218AEBDA2004A7AAE /* Supporting Files */,
 				1FF8DBB01FBA9DE1009DE660 /* dummy.cpp */,
@@ -254,14 +230,6 @@
 				1F1575721F582BE20003B888 /* dylibs in Resources */,
 				D07CD44E1C5D589C00B7FB28 /* Images.xcassets in Resources */,
 				D0BCFE7818AEBFEB004A7AAE /* $binary.pck in Resources */,
-				D07CD4471C5D573600B7FB28 /* Default-Portrait@2x.png in Resources */,
-				D07CD4461C5D573600B7FB28 /* Default-Landscape-1366h@2x.png in Resources */,
-				D07CD4411C5D573600B7FB28 /* Default-667h@2x.png in Resources */,
-				D07CD43F1C5D573600B7FB28 /* Default-568h@2x.png in Resources */,
-				D07CD4451C5D573600B7FB28 /* Default-Landscape@2x.png in Resources */,
-				D07CD4441C5D573600B7FB28 /* Default-Landscape-736h@3x.png in Resources */,
-				D07CD4421C5D573600B7FB28 /* Default-Portrait-736h@3x.png in Resources */,
-				D07CD4481C5D573600B7FB28 /* Default-Portrait-1366h@2x.png in Resources */,
 				D0BCFE4618AEBDA2004A7AAE /* InfoPlist.strings in Resources */,
 				$additional_pbx_resources_build
 			);
@@ -377,6 +345,7 @@
 			buildSettings = {
 				ARCHS = "$godot_archs";
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
 				CODE_SIGN_IDENTITY = "$code_sign_identity_debug";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_debug";
@@ -401,6 +370,7 @@
 			buildSettings = {
 				ARCHS = "$godot_archs";
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CODE_SIGN_ENTITLEMENTS = $binary/$binary.entitlements;
 				CODE_SIGN_IDENTITY = "$code_sign_identity_release";
 				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "$code_sign_identity_release";
diff --git a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png
deleted file mode 100644
index 1d5e472665d..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-568h@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png
deleted file mode 100644
index b51598fed08..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-667h@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png
deleted file mode 100644
index ec5b4f78881..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Landscape-1366h@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png
deleted file mode 100644
index 2a025b745b3..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Landscape-736h@3x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png
deleted file mode 100644
index 7099f3e18d0..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Landscape@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png
deleted file mode 100644
index a6d054ba2ab..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Portrait-1366h@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png
deleted file mode 100644
index 8c44edbccde..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Portrait-736h@3x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png
deleted file mode 100644
index a6d054ba2ab..00000000000
Binary files a/misc/dist/ios_xcode/godot_ios/Default-Portrait@2x.png and /dev/null differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
new file mode 100644
index 00000000000..ce81d76027a
--- /dev/null
+++ b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Contents.json
@@ -0,0 +1,102 @@
+{
+  "images" : [
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "2436h",
+      "filename" : "Default-Portrait-X.png",
+      "minimum-system-version" : "11.0",
+      "orientation" : "portrait",
+      "scale" : "3x"
+    },
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "2436h",
+      "filename" : "Default-Landscape-X.png",
+      "minimum-system-version" : "11.0",
+      "orientation" : "landscape",
+      "scale" : "3x"
+    },
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "736h",
+      "filename" : "Default-Portrait-736h@3x.png",
+      "minimum-system-version" : "8.0",
+      "orientation" : "portrait",
+      "scale" : "3x"
+    },
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "736h",
+      "filename" : "Default-Landscape-736h@3x.png",
+      "minimum-system-version" : "8.0",
+      "orientation" : "landscape",
+      "scale" : "3x"
+    },
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "667h",
+      "filename" : "Default-667h@2x.png",
+      "minimum-system-version" : "8.0",
+      "orientation" : "portrait",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "iphone",
+      "filename" : "Default-480h@2x.png",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    },
+    {
+      "extent" : "full-screen",
+      "idiom" : "iphone",
+      "subtype" : "retina4",
+      "filename" : "Default-568h@2x.png",
+      "minimum-system-version" : "7.0",
+      "orientation" : "portrait",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "ipad",
+      "filename" : "Default-Portrait.png",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "1x"
+    },
+    {
+      "orientation" : "landscape",
+      "idiom" : "ipad",
+      "filename" : "Default-Landscape.png",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "1x"
+    },
+    {
+      "orientation" : "portrait",
+      "idiom" : "ipad",
+      "filename" : "Default-Portrait@2x.png",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    },
+    {
+      "orientation" : "landscape",
+      "idiom" : "ipad",
+      "filename" : "Default-Landscape@2x.png",
+      "extent" : "full-screen",
+      "minimum-system-version" : "7.0",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png
new file mode 100644
index 00000000000..6b9b10daaea
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-480h@2x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png
new file mode 100644
index 00000000000..50497496b7c
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-568h@2x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png
new file mode 100644
index 00000000000..1c489de69f5
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-667h@2x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png
new file mode 100644
index 00000000000..d82dfce9363
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-736h@3x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png
new file mode 100644
index 00000000000..5120595df85
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape-X.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png
new file mode 100644
index 00000000000..cf6cf1347ad
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png
new file mode 100644
index 00000000000..4eb167ae18a
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Landscape@2x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png
new file mode 100644
index 00000000000..a9f951deac7
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-736h@3x.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png
new file mode 100644
index 00000000000..06d16412e2e
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait-X.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png
new file mode 100644
index 00000000000..d70cab17bef
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait.png differ
diff --git a/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png
new file mode 100644
index 00000000000..f9349710742
Binary files /dev/null and b/misc/dist/ios_xcode/godot_ios/Images.xcassets/LaunchImage.launchimage/Default-Portrait@2x.png differ
diff --git a/platform/iphone/app_delegate.mm b/platform/iphone/app_delegate.mm
index 5c3799ab092..9e6bbff1d7d 100644
--- a/platform/iphone/app_delegate.mm
+++ b/platform/iphone/app_delegate.mm
@@ -79,6 +79,7 @@ static ViewController *mainViewController = nil;
 }
 
 NSMutableDictionary *ios_joysticks = nil;
+NSMutableArray *pending_ios_joysticks = nil;
 
 - (GCControllerPlayerIndex)getFreePlayerIndex {
 	bool have_player_1 = false;
@@ -115,6 +116,30 @@ NSMutableDictionary *ios_joysticks = nil;
 	};
 };
 
+void _ios_add_joystick(GCController *controller, AppDelegate *delegate) {
+	// get a new id for our controller
+	int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
+	if (joy_id != -1) {
+		// assign our player index
+		if (controller.playerIndex == GCControllerPlayerIndexUnset) {
+			controller.playerIndex = [delegate getFreePlayerIndex];
+		};
+
+		// tell Godot about our new controller
+		OSIPhone::get_singleton()->joy_connection_changed(
+				joy_id, true, [controller.vendorName UTF8String]);
+
+		// add it to our dictionary, this will retain our controllers
+		[ios_joysticks setObject:controller
+						  forKey:[NSNumber numberWithInt:joy_id]];
+
+		// set our input handler
+		[delegate setControllerInputHandler:controller];
+	} else {
+		printf("Couldn't retrieve new joy id\n");
+	};
+}
+
 - (void)controllerWasConnected:(NSNotification *)notification {
 	// create our dictionary if we don't have one yet
 	if (ios_joysticks == nil) {
@@ -127,28 +152,12 @@ NSMutableDictionary *ios_joysticks = nil;
 		printf("Couldn't retrieve new controller\n");
 	} else if ([[ios_joysticks allKeysForObject:controller] count] != 0) {
 		printf("Controller is already registered\n");
+	} else if (frame_count > 1) {
+		_ios_add_joystick(controller, self);
 	} else {
-		// get a new id for our controller
-		int joy_id = OSIPhone::get_singleton()->get_unused_joy_id();
-		if (joy_id != -1) {
-			// assign our player index
-			if (controller.playerIndex == GCControllerPlayerIndexUnset) {
-				controller.playerIndex = [self getFreePlayerIndex];
-			};
-
-			// tell Godot about our new controller
-			OSIPhone::get_singleton()->joy_connection_changed(
-					joy_id, true, [controller.vendorName UTF8String]);
-
-			// add it to our dictionary, this will retain our controllers
-			[ios_joysticks setObject:controller
-							  forKey:[NSNumber numberWithInt:joy_id]];
-
-			// set our input handler
-			[self setControllerInputHandler:controller];
-		} else {
-			printf("Couldn't retrieve new joy id\n");
-		};
+		if (pending_ios_joysticks == nil)
+			pending_ios_joysticks = [[NSMutableArray alloc] init];
+		[pending_ios_joysticks addObject:controller];
 	};
 };
 
@@ -352,6 +361,27 @@ NSMutableDictionary *ios_joysticks = nil;
 		[ios_joysticks dealloc];
 		ios_joysticks = nil;
 	};
+
+	if (pending_ios_joysticks != nil) {
+		[pending_ios_joysticks dealloc];
+		pending_ios_joysticks = nil;
+	};
+};
+
+OS::VideoMode _get_video_mode() {
+	int backingWidth;
+	int backingHeight;
+	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+			GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
+	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
+			GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+
+	OS::VideoMode vm;
+	vm.fullscreen = true;
+	vm.width = backingWidth;
+	vm.height = backingHeight;
+	vm.resizable = false;
+	return vm;
 };
 
 static int frame_count = 0;
@@ -360,19 +390,7 @@ static int frame_count = 0;
 
 	switch (frame_count) {
 		case 0: {
-			int backingWidth;
-			int backingHeight;
-			glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
-					GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
-			glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
-					GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
-
-			OS::VideoMode vm;
-			vm.fullscreen = true;
-			vm.width = backingWidth;
-			vm.height = backingHeight;
-			vm.resizable = false;
-			OS::get_singleton()->set_video_mode(vm);
+			OS::get_singleton()->set_video_mode(_get_video_mode());
 
 			if (!OS::get_singleton()) {
 				exit(0);
@@ -410,6 +428,14 @@ static int frame_count = 0;
 			Main::setup2();
 			++frame_count;
 
+			if (pending_ios_joysticks != nil) {
+				for (GCController *controller in pending_ios_joysticks) {
+					_ios_add_joystick(controller, self);
+				}
+				[pending_ios_joysticks dealloc];
+				pending_ios_joysticks = nil;
+			}
+
 			// this might be necessary before here
 			NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
 			for (NSString *key in dict) {
@@ -562,18 +588,13 @@ static int frame_count = 0;
 	//[glView setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
 	// UIViewAutoresizingFlexibleWidth];
 
-	int backingWidth;
-	int backingHeight;
-	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
-			GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
-	glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
-			GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
+	OS::VideoMode vm = _get_video_mode();
 
 	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
 			NSUserDomainMask, YES);
 	NSString *documentsDirectory = [paths objectAtIndex:0];
 
-	int err = iphone_main(backingWidth, backingHeight, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
+	int err = iphone_main(vm.width, vm.height, gargc, gargv, String::utf8([documentsDirectory UTF8String]));
 	if (err != 0) {
 		// bail, things did not go very well for us, should probably output a message on screen with our error code...
 		exit(0);
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index e3119814f4d..4c1e02baf7d 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -147,6 +147,26 @@ Vector<EditorExportPlatformIOS::ExportArchitecture> EditorExportPlatformIOS::_ge
 	return archs;
 }
 
+struct LoadingScreenInfo {
+	const char *preset_key;
+	const char *export_name;
+};
+
+static const LoadingScreenInfo loading_screen_infos[] = {
+	{ "landscape_launch_screens/iphone_2436x1125", "Default-Landscape-X.png" },
+	{ "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
+	{ "landscape_launch_screens/ipad_1024x768", "Default-Landscape.png" },
+	{ "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
+
+	{ "portrait_launch_screens/iphone_640x960", "Default-480h@2x.png" },
+	{ "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
+	{ "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
+	{ "portrait_launch_screens/iphone_1125x2436", "Default-Portrait-X.png" },
+	{ "portrait_launch_screens/ipad_768x1024", "Default-Portrait.png" },
+	{ "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" },
+	{ "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" }
+};
+
 void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) {
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), ""));
@@ -172,6 +192,7 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/iphone_120x120", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone/iPod Touch with retina display
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/ipad_76x76", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad
+	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "required_icons/app_store_1024x1024", PROPERTY_HINT_FILE, "png"), "")); // App Store
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/iphone_180x180", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPhone with retina HD display
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/ipad_152x152", PROPERTY_HINT_FILE, "png"), "")); // Home screen on iPad with retina display
@@ -179,15 +200,9 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_40x40", PROPERTY_HINT_FILE, "png"), "")); // Spotlight
 	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "optional_icons/spotlight_80x80", PROPERTY_HINT_FILE, "png"), "")); // Spotlight on devices with retina display
 
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/iphone_2208x1242", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2732x2048", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "landscape_launch_screens/ipad_2048x1536", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
-
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_640x1136", PROPERTY_HINT_FILE, "png"), "")); // iPhone 5, 5s, SE
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_750x1334", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6, 6s, 7
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/iphone_1242x2208", PROPERTY_HINT_FILE, "png"), "")); // iPhone 6 Plus, 6s Plus, 7 Plus
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_2048x2732", PROPERTY_HINT_FILE, "png"), "")); // 12.9-inch iPad Pro
-	r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "portrait_launch_screens/ipad_1536x2048", PROPERTY_HINT_FILE, "png"), "")); // Other iPads
+	for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
+		r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, loading_screen_infos[i].preset_key, PROPERTY_HINT_FILE, "png"), ""));
+	}
 
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/s3tc"), false));
 	r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "texture_format/etc"), false));
@@ -313,6 +328,7 @@ static const IconInfo icon_infos[] = {
 	{ "required_icons/iphone_120x120", "iphone", "Icon-120.png", "120", "3x", "40x40", true },
 
 	{ "required_icons/ipad_76x76", "ipad", "Icon-76.png", "76", "1x", "76x76", false },
+	{ "required_icons/app_store_1024x1024", "ios-marketing", "Icon-1024.png", "1024", "1x", "1024x1024", false },
 
 	{ "optional_icons/iphone_180x180", "iphone", "Icon-180.png", "180", "3x", "60x60", false },
 
@@ -380,23 +396,6 @@ Error EditorExportPlatformIOS::_export_icons(const Ref<EditorExportPreset> &p_pr
 	return OK;
 }
 
-struct LoadingScreenInfo {
-	const char *preset_key;
-	const char *export_name;
-};
-
-static const LoadingScreenInfo loading_screen_infos[] = {
-	{ "landscape_launch_screens/iphone_2208x1242", "Default-Landscape-736h@3x.png" },
-	{ "landscape_launch_screens/ipad_2732x2048", "Default-Landscape-1366h@2x.png" },
-	{ "landscape_launch_screens/ipad_2048x1536", "Default-Landscape@2x.png" },
-
-	{ "portrait_launch_screens/iphone_640x1136", "Default-568h@2x.png" },
-	{ "portrait_launch_screens/iphone_750x1334", "Default-667h@2x.png" },
-	{ "portrait_launch_screens/iphone_1242x2208", "Default-Portrait-736h@3x.png" },
-	{ "portrait_launch_screens/ipad_2048x2732", "Default-Portrait-1366h@2x.png" },
-	{ "portrait_launch_screens/ipad_1536x2048", "Default-Portrait@2x.png" }
-};
-
 Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPreset> &p_preset, const String &p_dest_dir) {
 	DirAccess *da = DirAccess::open(p_dest_dir);
 	ERR_FAIL_COND_V(!da, ERR_CANT_OPEN);
@@ -404,12 +403,14 @@ Error EditorExportPlatformIOS::_export_loading_screens(const Ref<EditorExportPre
 	for (int i = 0; i < sizeof(loading_screen_infos) / sizeof(loading_screen_infos[0]); ++i) {
 		LoadingScreenInfo info = loading_screen_infos[i];
 		String loading_screen_file = p_preset->get(info.preset_key);
-		Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
-		if (err) {
-			memdelete(da);
-			String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
-			ERR_PRINT(err_str.utf8().get_data());
-			return err;
+		if (loading_screen_file.size() > 0) {
+			Error err = da->copy(loading_screen_file, p_dest_dir + info.export_name);
+			if (err) {
+				memdelete(da);
+				String err_str = String("Failed to export loading screen (") + info.preset_key + ") from path: " + loading_screen_file;
+				ERR_PRINT(err_str.utf8().get_data());
+				return err;
+			}
 		}
 	}
 	memdelete(da);
@@ -887,7 +888,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p
 	if (err)
 		return err;
 
-	err = _export_loading_screens(p_preset, dest_dir + binary_name + "/");
+	err = _export_loading_screens(p_preset, dest_dir + binary_name + "/Images.xcassets/LaunchImage.launchimage/");
 	if (err)
 		return err;
 
diff --git a/platform/iphone/gl_view.h b/platform/iphone/gl_view.h
index 85376ebc086..0d101eb696d 100644
--- a/platform/iphone/gl_view.h
+++ b/platform/iphone/gl_view.h
@@ -83,6 +83,8 @@
 @property(strong, nonatomic) MPMoviePlayerController *moviePlayerController;
 @property(strong, nonatomic) UIWindow *backgroundWindow;
 
+@property(nonatomic) UITextAutocorrectionType autocorrectionType;
+
 - (void)startAnimation;
 - (void)stopAnimation;
 - (void)drawView;
diff --git a/platform/iphone/gl_view.mm b/platform/iphone/gl_view.mm
index 69116c64c6f..ff95cf6e5fb 100644
--- a/platform/iphone/gl_view.mm
+++ b/platform/iphone/gl_view.mm
@@ -78,6 +78,16 @@ void _hide_keyboard() {
 	keyboard_text = "";
 };
 
+Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height) {
+	UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, 0, 0);
+	if (_instance != nil && [_instance respondsToSelector:@selector(safeAreaInsets)]) {
+		insets = [_instance safeAreaInsets];
+	}
+	ERR_FAIL_COND_V(insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0,
+			Rect2(0, 0, p_window_width, p_window_height));
+	return Rect2(insets.left, insets.top, p_window_width - insets.right - insets.left, p_window_height - insets.bottom - insets.top);
+}
+
 bool _play_video(String p_path, float p_volume, String p_audio_track, String p_subtitle_track) {
 	p_path = ProjectSettings::get_singleton()->globalize_path(p_path);
 
@@ -326,9 +336,7 @@ static void clear_touches() {
 	// Generate IDs for a framebuffer object and a color renderbuffer
 	UIScreen *mainscr = [UIScreen mainScreen];
 	printf("******** screen size %i, %i\n", (int)mainscr.currentMode.size.width, (int)mainscr.currentMode.size.height);
-	float minPointSize = MIN(mainscr.bounds.size.width, mainscr.bounds.size.height);
-	float minScreenSize = MIN(mainscr.currentMode.size.width, mainscr.currentMode.size.height);
-	self.contentScaleFactor = minScreenSize / minPointSize;
+	self.contentScaleFactor = mainscr.nativeScale;
 
 	glGenFramebuffersOES(1, &viewFramebuffer);
 	glGenRenderbuffersOES(1, &viewRenderbuffer);
@@ -627,6 +635,7 @@ static void clear_touches() {
 	}
 	init_touches();
 	self.multipleTouchEnabled = YES;
+	self.autocorrectionType = UITextAutocorrectionTypeNo;
 
 	printf("******** adding observer for sound routing changes\n");
 	[[NSNotificationCenter defaultCenter]
diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp
index c4c59431f2a..97ebc193355 100644
--- a/platform/iphone/os_iphone.cpp
+++ b/platform/iphone/os_iphone.cpp
@@ -504,6 +504,12 @@ Size2 OSIPhone::get_window_size() const {
 	return Vector2(video_mode.width, video_mode.height);
 }
 
+extern Rect2 _get_ios_window_safe_area(float p_window_width, float p_window_height);
+
+Rect2 OSIPhone::get_window_safe_area() const {
+	return _get_ios_window_safe_area(video_mode.width, video_mode.height);
+}
+
 bool OSIPhone::has_touchscreen_ui_hint() const {
 
 	return true;
diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h
index 2e4458aeed8..00d9efb01a2 100644
--- a/platform/iphone/os_iphone.h
+++ b/platform/iphone/os_iphone.h
@@ -177,6 +177,7 @@ public:
 	virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot);
 
 	virtual Size2 get_window_size() const;
+	virtual Rect2 get_window_safe_area() const;
 
 	virtual bool has_touchscreen_ui_hint() const;