SwiftUI - 画布超出屏幕宽度 - 应用被拒绝

Posted

技术标签:

【中文标题】SwiftUI - 画布超出屏幕宽度 - 应用被拒绝【英文标题】:SwiftUI - Canvas exceeding screen width - App Rejected 【发布时间】:2019-10-25 01:32:48 【问题描述】:

基于登录屏幕超出 iPad 显示屏边界的问题,我的应用在提交到应用商店时被拒绝了两次。我已尝试在模拟器或物理 iPad 上重现此问题

Apple 向我发送了以下回复

From Apple
4. Design: Preamble
Guideline 4.0 - Design

We noticed that the login screen of your app was still crowded or laid out in a way that made it difficult to use your app.
We launched the app on iPad (6th generation) running ios 13.1.3 on Wifi.

Next Steps
To resolve this issue, please revise your app to ensure that the content and controls on the screen are easy to read and interact with.

Resources
For more information, please review the following resources on the iOS Developer Center page:

   - UI Do's and Don'ts
   - iOS Human Interface Guidelines
   - UIKit

Please see attached screenshot for details.

我无法复制这个。这是我的代码:

登录.swift

import SwiftUI

struct Login: View 

    @EnvironmentObject var session: SessionStore
    @ObservedObject private var kGuardian = KeyboardGuardian(textFieldCount: KeyboardGuardian.KeyboardSlots.count.rawValue)

    @State var email: String = ""
    @State var password: String = ""
    @State var loading = false
    @State var error = false


    func getUser () 
        session.listen()
    

    func signIn () 
        loading = true
        error = false
        session.signIn(email: email, password: password)  (result, error) in
            self.loading = false
            if error != nil 
                self.error = true
             else 
                self.email = ""
                self.password = ""
            
        
    

    var body: some View 
        VStack 
            Image("Cloud Pager")
                .resizable()
                .foregroundColor(.white)
                .aspectRatio(contentMode: ContentMode.fill)
                .padding()

            Text("Cloud Pager")
                .font(.largeTitle)
                .foregroundColor(.white)
                .bold()
                .padding(Edge.Set.bottom, 30)

            Spacer()

            ZStack(alignment: .leading) 
                Text("Email")
                    .foregroundColor(email.isEmpty ? .white : .black)
                    .fontWeight(email.isEmpty ? .regular : .bold)
                    .padding(.leading, 10)
                    .offset(y: email.isEmpty ? 0 : -30)

                TextField("", text:$email)
                    .background(Color.clear)
                    .foregroundColor(.white)
                    .textContentType(.emailAddress)
                    .keyboardType(.emailAddress)
                    .padding(.leading, 10)
                    .padding(.trailing, 10)
            
            Rectangle()
                .frame(height: 3.0, alignment: .bottom)
                .foregroundColor(Color.white)
                .padding(.bottom, 40)
                .padding(.leading, 10)
                .padding(.trailing, 10)

            ZStack(alignment: .leading) 
                Text("Password")
                    .foregroundColor(password.isEmpty ? .white : .black)
                    .fontWeight(password.isEmpty ? .regular : .bold)
                    .padding(.leading, 10)
                    .offset(y: password.isEmpty ? 0 : -30)
                SecureField("", text:$password)
                    .background(Color.clear)
                    .foregroundColor(.white)
                    .textContentType(.password)
                    .padding(.leading, 10)
                    .padding(.trailing, 10)
            
            Rectangle()
                .frame(height: 3.0, alignment: .bottom)
                .foregroundColor(Color.white)
                .padding(.bottom, 50)
                .padding(.leading, 10)
                .padding(.trailing, 10)

            VStack 
                Button(action: signIn) 
                    HStack(alignment: .center) 
                        Spacer()
                        if loading 
                            ActivityIndicator()
                         else 
                            Text("Sign In")
                                .foregroundColor(.blue)
                                .bold()
                        
                        Spacer()
                    
                .padding().background(Color.white).cornerRadius(30.0)
            
            .padding(Edge.Set.bottom, 8)
        
        .padding()
        .background(Color.blue.edgesIgnoringSafeArea(.all))
        .statusBar(hidden: true)
        .onAppear(perform: getUser)
        .offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.5))
    


struct ActivityIndicator: UIViewRepresentable 

    func makeUIView(context: Context) -> UIActivityIndicatorView 
        let v = UIActivityIndicatorView()

        return v
    

    func updateUIView(_ activityIndicator: UIActivityIndicatorView, context: Context) 
        activityIndicator.startAnimating()
    


struct Login_Previews: PreviewProvider 
    static var previews: some View 
        Login()
            .environmentObject(SessionStore())
    

在我运行应用程序的每个 iPad 模拟器(以及物理的第 6 代 iPad)上,我无法复制上面的屏幕截图。我的总是这样:

谁能弄清楚这里发生了什么。我的应用设置设置为仅在 iPhone 和 iPad 设备上的纵向

Info.plist

<?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>$(DEVELOPMENT_LANGUAGE)</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
    <key>CFBundleShortVersionString</key>
    <string>$(MARKETING_VERSION)</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>UIApplicationSceneManifest</key>
    <dict>
        <key>UIApplicationSupportsMultipleScenes</key>
        <false/>
        <key>UISceneConfigurations</key>
        <dict>
            <key>UIWindowSceneSessionRoleApplication</key>
            <array>
                <dict>
                    <key>UISceneConfigurationName</key>
                    <string>Default Configuration</string>
                    <key>UISceneDelegateClassName</key>
                    <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
                </dict>
            </array>
        </dict>
    </dict>
    <key>UIBackgroundModes</key>
    <array>
        <string>remote-notification</string>
    </array>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
        <string>armv7</string>
    </array>
    <key>UIRequiresFullScreen</key>
    <true/>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
    </array>
    <key>UISupportedInterfaceOrientations~ipad</key>
    <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationPortraitUpsideDown</string>
    </array>
</dict>
</plist>

【问题讨论】:

如果你创建一个最小的项目并添加一个链接而不是这个依赖代码会很好。 我在 github 上有整个项目,但由于它包括 firebase 配置文件,所以 repo 是私有的。 不需要整个项目。只是他的页面链接到静态数据。 我不完全确定你要的是什么。 整个答案只是改变了两个字符。 fill -> fit 【参考方案1】:

问题在于图像配置。您应该在其中添加以下修饰符:

.scaledToFit()

只是将内容模式更改为适合

.aspectRatio(contentMode: .fit)

之前 -> 之后

请注意,所有内容都无法调整大小,最好将其中一些设为固定大小。

建议:

对于这样的页面,将所有内容放在一个容器中,并给它maxWidthmaxHeight。您不希望您的设计扩展到使用第二个屏幕选项显示您的应用的整个 100 英寸电视,是吗?

注意 2:您使用的图像非常接近来自 Apple 的 受版权保护 图标,下次苹果可能会因为以下原因拒绝您的应用那。保重。

【讨论】:

感谢您的回答。你如何让模拟器在画布之外缩放?我想测试你的答案,但我一开始就无法复制这个问题。 将此代码(在问题中)复制到一个全新的项目中并修复所有错误以使其独立。 创建了一个新项目。放入代码并修复错误。在 iPad 6th gen 和 iPhone 11 模拟器上编译并运行。两者都没有超过你的画布https://imgur.com/a/0f2Llhp 已排序。它没有在我的模拟器上显示的原因是我运行的是 xcode 11 而不是 11.1,因为 11.1 安装有错误。删除了安装并升级到 11.1,它显示了扩展的画布,就像你的一样。答案很完美,干杯。 我会在允许的时候奖励赏金(10 小时)【参考方案2】:

有几件事。

是 .aspectRatio(contentMode: ContentMode.fill) 将顶层 VStack 扩展到屏幕边界之外。

所以你必须把它改成:

.aspectRatio(contentMode: ContentMode.fit)

这似乎可以解决问题:

但是,如果你在模拟器中运行它并旋转 iPad 几次,你会发现你的蓝色背景没有正确重绘:

[

要解决这个问题,您可以将所有内容包装在 ZStack 中:

var body: some View 
    ZStack 
        Color.blue
        VStack 
            Image("Cloud Pager")
            // etc
        
        .padding()
        .statusBar(hidden: true)
        .onAppear(perform: getUser)
        .offset(y: kGuardian.slide).animation(.easeInOut(duration: 0.5))
       .edgesIgnoringSafeArea(.all)

【讨论】:

以上是关于SwiftUI - 画布超出屏幕宽度 - 应用被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

如何使视图最大。 SwiftUI 中带填充的宽度

更改 EaselJS 画布宽度而不缩放内容

有没有办法使用几何阅读器将我的 SwiftUI 图像的宽度定义为相对于屏幕尺寸?

SwiftUI 画布预览为一个视图显示多个视图

SwiftUI如何对齐大于屏幕宽度的视图

如何在溢出屏幕的 SwiftUI 中制作无限宽度的视图?