-3

In iOS Settings > Music > Apple Music and Privacy, there is a modal that I've mostly successfully duplicated except in two ways (a brief demo of Apple's modal is here). First, I don't know how to duplicate the bullet points seen in the beginning of the text. Second, I don't know how to hide the nav bar title until scrolling past the logo and modal title. That behavior matches the .inline nav title style and suggests the logo and title are actually part of the nav bar title.

The answer here was really helpful in actually getting the nav bar to show up. I also tried using ToolbarItem and .principal from here to add an image to the toolbar, but that doesn't give the desired result. I tried adding padding because the image was squished into the nav bar but that didn't work. Lastly, I tried a VStack in the .principal placement to add the image and text below it, but that didn't work either.

Here's the code in the parent view:

struct ModalNavBar: View {
    @State private var showApplePolicy = false
    
    var body: some View {
        Button() {
            showApplePolicy = true
        } label: {
            Text("Apple Music and Privacy")
        }.sheet(isPresented: $showApplePolicy, content: {
            NavigationView {
                TermsAndPrivacyView()
                    .navigationTitle("Apple Music & Privacy")
                    .navigationBarTitleDisplayMode(.inline)
                    .toolbar {
                        ToolbarItem(placement: .principal) {
                            VStack {
                                Image("privacy")
                                    .resizable()
                                    .frame(width: 80, height: 65)
                                    .padding()
                                    .padding(.vertical, 20)
                                Text("Apple Music & Privacy")
                            }
                        }
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button(action: {
                                self.showApplePolicy = false
                            }) {
                                Text("Done").bold()
                            }
                        }
                    }
            }
        })
    }
}

and the modal view:

struct TermsAndPrivacyView: View {
    var body: some View {
        ScrollView {
            VStack(alignment: .center, spacing: 20) {
                Group {
                    Image("privacy")
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 80, height: 65)
                        .padding()
                        .padding(.top, 20)
                        .padding(.bottom, 5)
                    Text("Apple Music & Privacy")
                        .font(.title).bold().padding(.top, -20)
                    VStack(alignment: .center) {
                        Text("Apple Music is designed to protect your information and enable you to choose what you share.")
                    }
                }
            }.padding(.horizontal, 30)
        }
    }
}

Any help with the nav bar would be appreciated.

Alex
  • 11
  • 1
  • 2
    Welcome to Stack Overflow! Please take the [tour](https://stackoverflow.com/tour) and see: [How do I ask a good question?](https://stackoverflow.com/help/how-to-ask) and [How to create a Minimal, Reproducible Example (MRE)](https://stackoverflow.com/help/minimal-reproducible-example). You are missing a lot of relevant code such as the `TermsAndPrivacyView`. Also, it would be nice if you mocked up the view that will display the sheet with a simple button to show it. Otherwise, we have to spend time doing it. – Yrb Mar 26 '22 at 14:37
  • Thanks, and sorry about that. I've added a button to the parent view to display the sheet and the `TermsandPrivacyView` code. – Alex Mar 26 '22 at 14:55
  • Your example image expired. You should upload them directly to SO. – Yrb Mar 26 '22 at 16:09

1 Answers1

0

Firstly, This question should have been broken up into multiple questions. Normally that would have gotten the question closed, but here people only downvoted this. Make sure to read the links that I put in my first comment for future questions. Your questions are:

  1. How do I put place two texts or images next to each other and align them?
  2. How do I hide and show the .navigationTitle when a certain view is fully hidden behind the navigation bar?

In answer to the first question, I used a mix of .firstTextBaseline alignment and an .alignmentGuide to put the text bullet in the correct spot. While I hardcoded these values, try to avoid it if things can change. In this case, the view is unchanging so it is acceptable.

As to the hiding and showing the title, I used a PreferenceKey to read the location of the bottom of the Text("Apple Music & Privacy") view in the ScrollView. When that hits zero, this view is behind the navigation bar. Then I simply set a @State variable to toggle when this happens, and use that to set the title.

You will also notice I pulled everything that was in your ModalNavBar out. In a case like this, everything should be contained in the view that the sheet is displaying. It works better, and encapsulates things so you could just drop a different view into the sheet and it still works fine.

Lastly, I have omitted a bunch of text views to keep the answer brief. Feel free to add them back in. When you see the ellipsis, that simply means omitted code.

struct ModalNavBar: View {
    @State private var showApplePolicy = false
    
    var body: some View {
        Button() {
            showApplePolicy = true
        } label: {
            Text("Apple Music & Privacy")
        }.sheet(isPresented: $showApplePolicy, content: {
            NavigationView {
                TermsAndPrivacyView()
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button(action: {
                                self.showApplePolicy = false
                            }) {
                                Text("Done").bold()
                            }
                        }
                    }
            }
        })
    }
}

struct TermsAndPrivacyView: View {
    @State var showTitle = false
    var body: some View {
        ScrollView {
            VStack(alignment: .center, spacing: 20) {
                Image(systemName: "person.3.fill")
                    .font(.system(size: 80))
                Text("Apple Music & Privacy")
                    .font(.title).bold()
                    // The PreferenceKey reads where it is within the ScrollView. "Scroll" is a coordinate space name
                    // set on the ScrollView. .maxY is simply the value of the Y dimension at the bottom of that view
                    // within the coordinates of the ScrollView. When that hits zero, it is behind the navigation bar.
                    .background(
                        GeometryReader {
                            Color.clear.preference(key: TextLocationPrefKey.self,
                                                   value: $0.frame(in: .named("Scroll")).maxY)
                        }
                    )
                Text("Apple Music is designed to protect your information and enable you to choose what you share.")
                // Put the text bullet and text together in an HStack with a .firstTextBaseline alignement
                HStack(alignment: .firstTextBaseline) {
                    Text(Image(systemName: "circle.fill"))
                        .font(.system(size: 4))
                        .opacity(0.5)
                        // The .alignmentGuide let's you tweak the actual alignment. In this case I moved it up by 2.
                        .alignmentGuide(.firstTextBaseline) { context in
                            context.height + 2
                        }
                    Text("Lorem ipsum dolor sit amet, praesent necessitatibus ei has, te sit hinc munere, ea vix fugit novum noluisse. Nulla graeci delicatissimi qui cu, nec doming iudicabit ex, indoctum partiendo an has. In qui sensibus dissentiunt, inani iudico accusamus ei eum. Suas vidit primis vel ad, meis ignota postea at pri, ei usu consul evertitur. Per vocent sadipscing et. Has debitis deterruisset ei, democritum scribentur te duo, purto accommodare id ius.")
                }

                     ...                

            }.padding(.horizontal, 30)
        }
        // This is a simple ternary condition. If showTitle is true the title is set to "Apple Music & Privacy" else ""
        .navigationTitle(showTitle ? "Apple Music & Privacy" : "")
        .navigationBarTitleDisplayMode(.inline)
        // This is where the PreferenceKey value is compared to zero to set the showTitle variable.
        .onPreferenceChange(TextLocationPrefKey.self) { textLocation in
            withAnimation(.easeIn(duration: 0.15)) {
                showTitle = !(textLocation > 0)
            }
        }
        // This is where you select and name the view for the coordinate space
        .coordinateSpace(name: "Scroll")
    }
}

// This is how the preference key is set up.
fileprivate struct TextLocationPrefKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        // This adds or subtracts based on the change in the value as you scroll the view towards the navigation
        // bar, nextValue() is negative, reducing value towards zero.
        value += nextValue()
    }
}
Yrb
  • 8,103
  • 2
  • 14
  • 44