TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

SOLVED: How to let views remain the same on different iPhone screens

Forums > SwiftUI

Okay, so the design is a Tabview with various views and a simple tabbar.

And a section follows that with a header filled with texts in a white rectangular shape and a logo on top of the rectangle.

And I used .offset(x:, y,) to shift the header to remain on top permanently and the same .offset(x:, y,) to help keep the tabbar down permanently.

It worked fine on my iPhone 15 pro. But when tested on an iPhone 13 pro max, they are all jumping around. Please, is there a better way to fix this?

Sample code below:


var body: some View {

        //TabView Pages

        TabView {

                //HomeView
                HomeView()
                    .tabItem {
                        Label("Home", systemImage: "house.circle")
                    }
                //MediaView
                MediaView()
                    .tabItem {
                        Label("Media", systemImage: "tv.and.mediabox")
                    }
                //VideosView
                VideosView()
                    .tabItem {
                        Label("Videos", systemImage: "video")
                    }
                //ContactView
                ContactView()
                    .tabItem {
                        Label("Contact", systemImage: "person.crop.circle.fill")

            }

        }

        //Tab Customisation
        .onAppear() {

            UITabBar.appearance().backgroundColor = .purple
            UITabBar.appearance().unselectedItemTintColor = .bubbleGumPink

        }
        .tint(.white)
        .ignoresSafeArea()
        .offset(x: 0, y:200)

  //Header BG

        .background(
            Image("Header")
                .resizable()
                .frame(width: 1080, height: 300)
                .offset(x: 0, y:-300))

//HeaderText

        Section {
            ZStack {
                HStack {
                    VStack (alignment: .leading) {

                        Spacer()
                            .frame(height: 50)
                        Text("SOME HEADER TEXT")
                            .font(
                                Font.custom("Avenir Next", size: 20))
                            .bold()

                        Text("Subtitle...")
                            .font(
                                Font.custom("Avenir Next", size: 17))

 //White Rectangle BG

                    .frame(maxWidth: .infinity)
                    .background(
                        RoundedRectangle(cornerRadius: 20)
                            .foregroundColor(.white).shadow(radius: 20))

  //Logo
                    .overlay(Image("logo")
                        .resizable()
                        .frame(width: 70, height: 70)
                        .offset(x:-140, y:-90))
                }

            } }
        .offset(x: 0, y:-550)
    }

Can anyone please help me?

   

Anyone to help, please?

   

Hello,

I'm new with SwiftUI, but maybe GeometryReader can help to fix this.
Have a look at https://www.hackingwithswift.com/quick-start/swiftui/how-to-provide-relative-sizes-using-geometryreader and give it a try.

Regards, Ralf

1      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

@ralfb1105 Thanks so much. Will try it by GOD'S Grace, please 

   

Any other ideas, please?

   

You'd better post the image how it looks like, as from the code it is hard to understand what you're trying to achieve taking into account all those offsets and hard coded limitations for the view... I am sure there is an easy way to layout all the elements, as SwiftUI is really really flexible in that sense.

1      

Maybe this will be close to your view layout.

struct ContentView: View {
    var body: some View {
        VStack {
            ZStack(alignment: .bottomLeading) {
                // This may be your header image
                Color.orange.opacity(0.2)
                    .frame(height: 300)
                    .overlay(
                        GeometryReader { geometry in
                            // This may be your logo
                            Image(systemName: "heart")
                                .resizable()
                                .frame(width: 70, height: 70)
                                .position(x: geometry.size.width * 0.1, y: geometry.size.height * 0.6)
                        }
                    )

                VStack {
                    Text("SOME HEADER TEXT")
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .bold()
                        .padding(.horizontal, 8)

                    Text("Subtitle...")
                        .frame(maxWidth: .infinity, alignment: .leading)
                        .padding(.horizontal, 8)
                        .padding(.vertical, 4)
                        .background(
                            RoundedRectangle(cornerRadius: 20)
                                .foregroundColor(.white)
                                .shadow(radius: 5)
                        )
                }
                .padding(8)
            }

            TabView {

                //HomeView
                HomeView()
                    .tabItem {
                        Label("Home", systemImage: "house.circle")
                    }
                //MediaView
                MediaView()
                    .tabItem {
                        Label("Media", systemImage: "tv.and.mediabox")
                    }
                //VideosView
                VideosView()
                    .tabItem {
                        Label("Videos", systemImage: "video")
                    }
                //ContactView
                ContactView()
                    .tabItem {
                        Label("Contact", systemImage: "person.crop.circle.fill")

                    }
            }

        }

        //Tab Customisation
        .onAppear() {

            UITabBar.appearance().backgroundColor = .purple
            UITabBar.appearance().unselectedItemTintColor = .systemRed

        }
        .tint(.white)
        .ignoresSafeArea()
    }
}

1      

@ygeras Roger that. Will do so immediately. This is how the code looks like with the offsets

   

This is how it looks, please.

@ygeras Updating to add picture to the post...

   

I think this should be close to your layout now. But definitely use your buttons setup and other small details. But with this there shouldn't be any problems...

struct ContentView: View {
    var body: some View {
        VStack {
            ZStack(alignment: .bottomLeading) {
                // This may be your header image
                Color.orange.opacity(0.2)
                    .frame(height: 300)
                    .offset(y: -20)

                VStack(alignment: .leading, spacing: 10) {
                    Text("SOME HEADER TEXT")
                        .bold()

                    Text("Subtitle...")

                    HStack(spacing: 60) {
                        Button(action: { }, label: {
                            Label("Invite", systemImage: "person")
                        })
                        .buttonStyle(.borderedProminent)
                        .tint(.purple)

                        Button(action: { }, label: {
                            Label("Contact Us", systemImage: "envelope")
                        })
                        .buttonStyle(.borderedProminent)
                        .tint(.purple)
                    }
                    .frame(maxWidth: .infinity)

                }
                .padding(.horizontal, 16)
                .padding(.top, 60)
                .padding(.bottom, 16)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(
                    RoundedRectangle(cornerRadius: 20)
                        .foregroundColor(.white)
                        .shadow(radius: 5)
                )
                .overlay(
                    GeometryReader { geometry in
                        Circle()
                            .foregroundStyle(.purple)
                            .frame(width: 70, height: 70)
                            .position(x: geometry.size.width * 0.1, y: geometry.size.height * 0.05)
                    }
                )
            }

            TabView {

                //HomeView
                HomeView()
                    .tabItem {
                        Label("Home", systemImage: "house.circle")
                    }
                //MediaView
                MediaView()
                    .tabItem {
                        Label("Media", systemImage: "tv.and.mediabox")
                    }
                //VideosView
                VideosView()
                    .tabItem {
                        Label("Videos", systemImage: "video")
                    }
                //ContactView
                ContactView()
                    .tabItem {
                        Label("Contact", systemImage: "person.crop.circle.fill")

                    }
            }

        }

        //Tab Customisation
        .onAppear() {

            UITabBar.appearance().backgroundColor = .purple
            UITabBar.appearance().unselectedItemTintColor = .systemRed

        }
        .tint(.white)
        .ignoresSafeArea()
    }
}

1      

@ygeras Yay!!! Thanks so much. Will try it out and get back to you with the feedback.

So grateful

   

@ygeras Perfect! By GOD'S Grace it has worked! Thanks so much and GOD bless you.

But... before I let you go, is there a way to let the tabbar remain opaque even though a sroollable content is underneath it? XD

   

Update as follows tabbar appearance

//Tab Customisation
        .onAppear() {
            let appearance = UITabBarAppearance()
            appearance.configureWithOpaqueBackground()
            appearance.stackedLayoutAppearance.normal.iconColor = .red
            appearance.stackedLayoutAppearance.normal.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.red]

            appearance.stackedLayoutAppearance.selected.iconColor = .white
            appearance.stackedLayoutAppearance.selected.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]

            appearance.backgroundColor = .purple
            UITabBar.appearance().standardAppearance = appearance
            UITabBar.appearance().scrollEdgeAppearance = appearance
        }
        .tint(.white)
        .ignoresSafeArea()

   

Try to change alignment to .centerin this line

 VStack(alignment: .center, spacing: 10) {
                    Text("SOME HEADER TEXT")
                        .bold()

if the text is now visible it means your image takes more screen space then is visible to you. You need to add .resizable() to your image.

As for the scroll view try to remove .frame(height: 500). Webview should take all the space available and will adapt to different screen sizes as a result.

   

Is there any solution, please?

You may want to try to use .scaledToFit() or .scaledToFill() modifiers after applying .resizable() to the image.

Is there anyway to achieve this kind of button menu in the tabview?

Yes, but this does not belong to tabbar view. It is custom button with some animation. It takes some time to explain it, will try to find similar tutorial which I stumbled on recently.

   

Try to add .frame(maxWidth: .infinity, height: 300) and at the end .clipped(). It is because you have the image of certain size you have to find the best way to display it. At times it becomes tricky...

1      

Is there anyway to achieve this kind of button menu in the tabview?

You can play around with the below code. With some tweaks it may be close enough to the view on your picture...

UPD. Sightly modified to make distance between buttons perferct. PS border just for visual purposes.

struct ContentView: View {
    @State private var showButtons = false

    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            Color.blue.opacity(0.2)
                .ignoresSafeArea()

            Group {
                HStack(spacing: 6) {
                    Text("Facebook")
                        .font(.body)
                        .padding(.vertical, 12)
                        .padding(.horizontal, 20)
                        .background(
                            Color.white
                                .cornerRadius(10)
                                .shadow(radius: 5)
                        )

                    Button {
                        showButtons.toggle()
                    } label: {
                        Image(systemName: "face.smiling")
                            .frame(width: 70, height: 70)
                    }
                    .background(Circle().fill(Color.yellow))
                    .shadow(radius: 5)
                    .border(Color.black)
                }
                .opacity(showButtons ? 1 : 0)
                .offset(x: -5, y: showButtons ? -100 : 0)

                HStack(spacing: 6) {
                    Text("Email")
                        .font(.body)
                        .padding(.vertical, 12)
                        .padding(.horizontal, 20)
                        .background(
                            Color.white
                                .cornerRadius(10)
                                .shadow(radius: 5)
                        )

                    Button {
                        showButtons.toggle()
                    } label: {
                        Image(systemName: "envelope")
                            .frame(width: 70, height: 70)
                    }
                    .background(Circle().fill(Color.pink))
                    .shadow(radius: 5)
                    .border(Color.black)

                }
                .offset(x: -5, y: showButtons ? -190 : 0)
                .opacity(showButtons ? 1 : 0)

                HStack(spacing: 6) {
                    Text("Phone")
                        .font(.body)
                        .padding(.vertical, 12)
                        .padding(.horizontal, 20)
                        .background(
                            Color.white
                                .cornerRadius(10)
                                .shadow(radius: 5)
                        )

                    Button {
                        showButtons.toggle()
                    } label: {
                        Image(systemName: "phone")
                            .frame(width: 70, height: 70)
                    }
                    .background(
                        Circle().fill(Color.green))
                    .shadow(radius: 5)
                    .border(Color.black)
                }
                .opacity(showButtons ? 1 : 0)
                .offset(x: -5, y: showButtons ? -280 : 0)

                Button {
                    showButtons.toggle()
                } label: {
                    ZStack {
                        Image(systemName: "xmark")
                            .opacity(showButtons ? 1 : 0)
                            .rotationEffect(Angle(degrees: showButtons ? 0 : 180))

                        Image(systemName: "ellipsis")
                            .opacity(showButtons ? 0 : 1)
                            .rotationEffect(Angle(degrees: showButtons ? -180 : 0))
                    }
                    .frame(width: 80, height: 80)
                }
                .background(
                    Circle()
                        .fill(Color.blue)
                )
                .border(Color.black)

            }
            .padding(.trailing, 20)
            .font(.title)
            .tint(.white)
            .animation(.linear, value: showButtons)
        }
    }
}

1      

Thanks so much. Will try it out and get back to you, please XD

   

Not sure why you couldn't fix the image header but this is how it worksk in my case...

Image(.sampleBackground)
                    .resizable()
                    .frame(height: 300)
                    .clipShape(RoundedRectangle(cornerRadius: 20))

   

Still did not work... :'(

Please, what are the dimensions of your Image, maybe I should crop mine. And does your image show well for big and smaller iPhone screens too?

   

It was just random image i got from the internet 1848 × 782 pixels.

   

as for the buttons to attach to a view you may try to use the following approach:

.overlay(alignment: .bottomTrailing) {
         // buttons view placed here
 }

   

Just saw your new comment. Going to try it soon.

Which part of the view would be best to attach it to, please?

   

Maybe I should attach it to each tabview instead?

   

if you want the button to be present on each tab so attach overlay to tabbar itself like so

 TabView {
      //HomeView
      HomeView()
          .tabItem {
              Label("Home", systemImage: "house.circle")
          }
      //MediaView
      MediaView()
          .tabItem {
              Label("Media", systemImage: "tv.and.mediabox")
          }
      //VideosView
      VideosView()
          .tabItem {
              Label("Videos", systemImage: "video")
          }
      //ContactView
      ContactView()
          .tabItem {
              Label("Contact", systemImage: "person.crop.circle.fill")
          }
  }
  .overlay(alignment: .bottomTrailing) {
      // Buttons go here
          .padding(.bottom, 80) // will probably need some additional padding at the bottom
  }

1      

I am not really like this solution but it works for sure. Just repetitive code attached...

UPDATE: issue solved by wrapping tabs in group

 TabView {
    Group {
        //HomeView
        HomeView()
            .tabItem {
                Label("Home", systemImage: "house.circle")
            }

        //MediaView
        MediaView()
            .tabItem {
                Label("Media", systemImage: "tv.and.mediabox")
            }

        //VideosView
        VideosView()
            .tabItem {
                Label("Videos", systemImage: "video")
            }

        //ContactView
        ContactView()
            .tabItem {
                Label("Contact", systemImage: "person.crop.circle.fill")
            }
    }
    .overlay(
        Color(showButtons ? Color.white.opacity(0.8) : Color.clear)
    )
}
.overlay(alignment: .bottomTrailing) {
    // Buttons code goes here
        .padding(.bottom, 80)
}

1      

Roger that.

Will this work for the buttons?

// Link that will open Safari on iOS devices
    Link("Facebook", destination: URL(string: "https://www.facebook.com")!)

    //  Clickable telphone number
    Link("(800)555-1212", destination: URL(string: "tel:8005551212")!)

    //  Clickable Email Address
    Link("apple@me.com", destination: URL(string: "mailto:apple@me.com")!)

And is there a way to place a white opacity bg when the button is tapped upon? Like the picture I posted above, please?

And hopefully, my last question

In the Webview showing website pages, you have to tap for some seconds to get other links to work. Is there any solution to this?

   

And is there a way to place a white opacity bg when the button is tapped upon? Like the picture I posted above, please?

It was already udpated in here... https://www.hackingwithswift.com/forums/swiftui/how-to-let-views-remain-the-same-on-different-iphone-screens/26699/26744

Will this work for the buttons?

Shortly, yes. But refactoring needed.

In the Webview showing website pages, you have to tap for some seconds to get other links to work. Is there any solution to this?

No idea, how your Webview configured and set up, so cannot say anything...

   

@ygeras By GOD'S Grace, all is running smoothly-so wonderfully! I tried it on different devices and they are so clear!

What a blessing you are! I am eternally grateful.

All that is left is to try and figure out how to link the Call, Email, and Facebook. GOD willing I will research more on it.

GOD bless you once more!!!

Would you suggest Skip to help convert the app for android too, please?

I dont know what time it is where you are, but have a blessed night/day

   

@ygeras Before I close this, kindly permit me to bother you with two questions. The first is the most important. The second can be done without...

  1. We are using Button for our floating button. And the answers I find on how to add Email, Call, and Facebook all seem to use Navigation Link. Is there a way to get the same effect by just using buttons, please?
  2. And the second, is having to tap for a few seconds before the embeded webpage links open. This is the Webview code I use, please:

 //Webpage View

    struct Webview: UIViewControllerRepresentable {
        let url: URL
        func makeUIViewController(context: Context) -> WebviewController {
            let webviewController = WebviewController()
            let request = URLRequest(url: self.url, cachePolicy: .returnCacheDataElseLoad)
            webviewController.webview.load(request)
            return webviewController
        }
        func updateUIViewController(_ webviewController: WebviewController, context: Context) {
            //
        }
    }

    class WebviewController: UIViewController, WKNavigationDelegate {
        lazy var webview: WKWebView = WKWebView()
        lazy var progressbar: UIProgressView = UIProgressView()
        deinit {
            self.webview.removeObserver(self, forKeyPath: "estimatedProgress")
            self.webview.scrollView.removeObserver(self, forKeyPath: "contentOffset")
        }
        override func viewDidLoad() {
            super.viewDidLoad()
            self.webview.navigationDelegate = self
            self.view.addSubview(self.webview)
            self.webview.frame = self.view.frame
            self.webview.translatesAutoresizingMaskIntoConstraints = false
            self.view.addConstraints([
                self.webview.topAnchor.constraint(equalTo: self.view.topAnchor),
                self.webview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
                self.webview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
                self.webview.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            ])
            self.webview.addSubview(self.progressbar)
            self.setProgressBarPosition()
            webview.scrollView.addObserver(self, forKeyPath: "contentOffset", options: .new, context: nil)
            self.progressbar.progress = 0.1
            webview.addObserver(self, forKeyPath: "estimatedProgress", options: .new, context: nil)
        }
        func setProgressBarPosition() {
            self.progressbar.translatesAutoresizingMaskIntoConstraints = false
            self.webview.removeConstraints(self.webview.constraints)
            self.webview.addConstraints([
                self.progressbar.topAnchor.constraint(equalTo: self.webview.topAnchor, constant: self.webview.scrollView.contentOffset.y * -1),
                self.progressbar.leadingAnchor.constraint(equalTo: self.webview.leadingAnchor),
                self.progressbar.trailingAnchor.constraint(equalTo: self.webview.trailingAnchor),
            ])
        }
        // MARK: - Web view progress
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            switch keyPath {
            case "estimatedProgress":
                if self.webview.estimatedProgress >= 1.0 {
                    UIView.animate(withDuration: 0.3, animations: { () in
                        self.progressbar.alpha = 0.0
                    }, completion: { finished in
                        self.progressbar.setProgress(0.0, animated: false)
                    })
                } else {
                    self.progressbar.isHidden = false
                    self.progressbar.alpha = 1.0
                    progressbar.setProgress(Float(self.webview.estimatedProgress), animated: true)
                }
            case "contentOffset":
                self.setProgressBarPosition()
            default:
                super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
            }
        }
    }

It comes with a little progress bar on top which is pretty dandy. Only, I have to press a little longer to navigate to other web links/pages, please.

Xiexie ni.

   

We are using Button for our floating button. And the answers I find on how to add Email, Call, and Facebook all seem to use Navigation Link. Is there a way to get the same effect by just using buttons, please?

Instead of previous Button structs in my code change to this:

Link(destination: URL(string: "https://yahoo.com")!) {
    Image(systemName: "face.smiling")
        .frame(width: 70, height: 70)
}
.background(Circle().fill(Color.yellow))
.shadow(radius: 5)
.border(Color.black)
// This is necessary so that buttons are dismissed
// when navigation happens
.simultaneousGesture(
    TapGesture()
        .onEnded{ _ in
            showButtons = false
        }
)

of course using necessary values for email and tel. Test to be run on a real device. I have checked, all links work as intended.

   

And the second, is having to tap for a few seconds before the embeded webpage links open. This is the Webview code I use, please:

Not sure what you mean by this. With google.com link it works without a delay, so maybe all depends on the link or server you're trying to reach...

Would you suggest Skip to help convert the app for android too, please?

I am not an expert in android so cannot suggest anything in that relation.

   

Perfect! Problem solved by GOD'S Grace... :)

GOD bless you immensely.

   

@ygeras

Sorry, I had to come back to you again, as I did not receive responses when I posted elsewhere.

Firstly, I apologise for my ignorance. :|

I feel really clueless about this. The app was first built by a third party with my developer account, and our contract has expired, making me endeavour to make it myself by GOD’S Grace.

Now, I want to publish a new version (not a new app).

This shows up in the tabs of the Apple Developer page, the one with the yellow. Which shows the app is ready for a new submission.

https://share.icloud.com/photos/082Alt21FPqs8BcAjfET1uYjQ

And I have this in the app info: https://share.icloud.com/photos/068L_dtOVG20XQw5MpMbSZiDQ

Am I to copy the details of code from the above, and paste it in Xcode? And how do I go about the process? Sorry, rookie here wanting to learn.

And for the app version number (the yellow in the Apple Developer Page) for the new submission has this code: 2.59778.1

Is that to be the new version number? Do I enter that into Xcode as well without making any changes, and where should I post it, please?

So sorry for being so ignorant of the process.

Thanks and GOD bless you. :)

   

Cannot help you here...

But maybe this article will shed some light on what to do.

https://developer.apple.com/help/app-store-connect/update-your-app/create-a-new-version/

   

TYSM!!! Followed the instructions and found it helpful :)

And may I pick your brain a little? Seems you are the one person I can bother this way XD

I am trying to grasp Firebase, and wonder how to use it to post announcements in the app.

To display the data in a text like a list form with bold, or italics... for example:

MEETING DAYS FOR THE WEEK:

  • Monday - 8 AM
  • Tuesday - 2 PM
  • Something I can update whenever I want. And have it show real time.

Like this, please,

https://imgur.com/a/VF6VEEx

   

Already have Firebase installed by GOD'S Grace, though

   

@ygeras Any ideas on the firebase, please?

   

Anything, please?

   

Please open another post on the subject. The name of the post does not reflect the issue you're trying to tackle. Inital post cannot solve all your issues at hand as you may see so many posts in here, so people will not bother to read all info and see what you're trying to handle. If no answers are posted by other members it means most probably no one has experience with firebase or/and your question is not clear enough to answer. Try to read documentation on firebase at https://firebase.google.com/docs/build or try to goggle your issue, or even use ChatGPT...

I do not have such extensive experience with Firebase, so cannot give you a hand here.

   

Roger that. Thanks so much for everything.

   

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.