Search code examples
iosswiftlayoutswiftui

Broken UI on devices with small screen size - SwiftUI


So basically I have already crafted my UI and it works just fine if it wasnt for the fact that my UI/Stacks do not seem to be fitting into devices with smaller screens such as the iPod Touch 7th gen or the iPhone SE 2nd gen. I was under the impression that if I avoided hardcoded frames and went with dynamic elements such as the spacing parameter on stacks, .padding, Spacer(), etc. then SwiftUI would do its layout magic and it would work across multiple screen sizes. Im not even using complex views here, its just a couple stacks & Text views.

Code:

struct EntireView: View {
var viewModel: ViewModel
var body: some View {
    VStack {
        HeadView(vm: viewModel)
        Rectangle()
            .frame(width: UIScreen.main.bounds.width - 20, height: 1)
        FieldView(viewModel: viewModel)
        ButtonView()
    }
}

}

struct HeadView: View {

var body: some View {
    VStack {
        VStack(spacing: 15) {
            Text("some text")
                .font(.custom(FontType.OpenSans.bold, size: 16))
            Text(vm.description)
                .font(.custom(FontType.OpenSans.semibold, size: 14))
        }
        .padding()
        
        VStack {
            HStack {
                VStack {
                    Text("some text")
                        .font(.custom(FontType.OpenSans.bold, size: 16))
                    Text("some text")
                        .font(.custom(FontType.OpenSans.regular, size: 12))
                }
                Spacer()
                VStack {
                    Text("some text")
                        .font(.custom(FontType.OpenSans.bold, size: 16))
                    Text(""some text")
                        .font(.custom(FontType.OpenSans.regular, size: 12))
                }
                Spacer()
                VStack {
                    Text("some text")
                        .font(.custom(FontType.OpenSans.bold, size: 16))
                    Text("some text")
                        .font(.custom(FontType.OpenSans.regular, size: 12))
                }
            }
        }
        .padding()
        HStack {
            Text("some text")
                .font(.custom(FontType.OpenSans.italic, size: 14))
            Spacer()
            Text("some text")
                .font(.custom(FontType.OpenSans.bold, size: 14))
                .foregroundColor(.white)
                .frame(width: 69, height: 25)
                .background(.green)
                .cornerRadius(4)
        }
        .padding()
    }
}

}

struct FieldView: View {
let viewModel: HoldingDetailViewModel

var body: some View {
    VStack(spacing: 20) {
        VStack(spacing: 10) {
            Text("some text")
                .foregroundColor(.blue)
            Text("some text")
                .foregroundColor(.blue)
        }
        
        VStack(spacing: 10) {
            Text("some text")
                .foregroundColor(.blue)
            HStack {
                Text("some text")
                Rectangle()
                    .fill(.blue)
                    .frame(width: 1, height: 25)
                Text("some text")
            }
            .foregroundColor(.blue)
        }
        
        VStack(spacing: 10) {
            Text("some text")
                .foregroundColor(.blue)
            Text("some text")
                .foregroundColor(.blue)
        }
        
        VStack(spacing: 10) {
            Text("some text")
                .foregroundColor(.blue)
            Text("some text")
                .foregroundColor(.blue)
        }
        
        VStack(spacing: 10) {
            Text("some text")
                .foregroundColor(.blue)
            Text("some text")
                .foregroundColor(.blue)
        }
        
    }
    .font(.custom(FontType.OpenSans.semibold, size: 14))
}

On smaller devices, theres elements not even being rendered as they do not fit the screen, how should I go about fixing this?


Solution

  • Currently, you have stacked so many Vertical Stacks on top of each other, that the UI builder simply runs out of physical screen estate to render all your UI elements.

    I think what you want to use is a ScrollView like this:

    struct EntireView: View {
        var viewModel: ViewModel
        var body: some View {
            ScrollView(.vertical, showsIndicators: false) {
                HeadView(vm: viewModel)
                Rectangle()
                    .frame(width: UIScreen.main.bounds.width - 20, height: 1)
                FieldView(viewModel: viewModel)
                ButtonView()
            }
        }
    }
    

    This way, all UI elements are rendered in a large VStack so to say, which you can scroll through. Thus, not limited to the phyiscal screen size. Is this what you wanted?

    If you desire some parts of your Views to be scrollable and some not, you can do the following for example:

    struct EntireView: View {
        var viewModel: ViewModel
        var body: some View {
            VStack {
                HeadView(vm: viewModel)
                ScrollView(.vertical, showsIndicators: false) {
                
                     Rectangle()
                         .frame(width: UIScreen.main.bounds.width - 20, height: 1)
                     FieldView(viewModel: viewModel)
                     ButtonView()
                 }
            }
            
        }
    }
    

    This way, the HeadView stays in position at the top of the screen and the other contents will be scrollable.