Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

"How to Center Text in the Middle of a Black Overlay in SwiftUI?"

I’m having trouble positioning text in the center of a black overlay in SwiftUI. I want the white text to be aligned in the middle of the black box where I marked with a red line in the image below. Despite trying different alignments and padding, I can’t seem to get the text to appear where I want it.

Here’s the image showing where I want the text to be:


  image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: customSize.width, height: customSize.height)
                            .clipped()
                            .overlay(
                                VStack(alignment: .leading) {
                                    Spacer()
                                    HStack {
                                        VStack(alignment: .leading) {
                                            if let name = channel.tvgName {
                                                Text(formatTitle(name))
                                                    .font(.title3.bold()) // Adjust the font size and style
                                                    .foregroundColor(.white)
                                                    .lineLimit(1)
                                                    .shadow(radius: 5) // Add shadow for better contrast
                                                    .padding(.all, 0)
                                            } else {
                                                Text(formatTitle(channel.tvgId ?? ""))
                                                    .font(.title3.bold()) // Adjust the font size and style
                                                    .foregroundColor(.white)
                                                    .lineLimit(1)
                                                    .shadow(radius: 5) // Add shadow for better contrast
                                                    .padding(.all, 0)
                                            }
                                        }
                                        
                                        Spacer()
                                    }
                                    .padding([.leading, .bottom, .trailing], 50)
                                    .background(LinearGradient(
                                        gradient: Gradient(colors: [Color.blue, Color.blue.opacity(0.1)]),
                                        startPoint: .bottom,
                                        endPoint: .top
                                    ))
                                }
                            )
                            
                            .cornerRadius(10)
                            .shadow(radius: 5)
                            

Full view code:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import SwiftUI
import CachedAsyncImage

struct ChannelDetailsView: View {
    var channel: PlaylistItem
    var reader: GeometryProxy
    @State var customSize: CGSize = CGSize(width: 256, height: 418) // Updated size
    @State private var showByGroup = UserDefaults.standard.bool(forKey: "showByGroup")

    var body: some View {
        VStack(spacing: 0) {
            if let logo = channel.tvgLogo {
                CachedAsyncImage(url: URL(string: logo)) { phase in
                    switch phase {
                    case .empty:
                        ProgressView()
                            .frame(maxWidth: .infinity, maxHeight: .infinity)
                            .background(Color.gray.opacity(0.3))
                            .cornerRadius(10)
                            .frame(width: customSize.width,
                                   height: customSize.height)
                    case .success(let image):
                        image
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: customSize.width, height: customSize.height)
                            .clipped()
                            .overlay(
                                VStack(alignment: .leading) {
                                    Spacer()
                                    HStack {
                                        VStack(alignment: .leading) {
                                            if let name = channel.tvgName {
                                                Text(formatTitle(name))
                                                    .font(.title3.bold()) // Adjust the font size and style
                                                    .foregroundColor(.white)
                                                    .lineLimit(1)
                                                    .shadow(radius: 5) // Add shadow for better contrast
                                                    .padding(.all, 0)
                                            } else {
                                                Text(formatTitle(channel.tvgId ?? ""))
                                                    .font(.title3.bold()) // Adjust the font size and style
                                                    .foregroundColor(.white)
                                                    .lineLimit(1)
                                                    .shadow(radius: 5) // Add shadow for better contrast
                                                    .padding(.all, 0)
                                            }
                                        }
                                        
                                        Spacer()
                                    }
                                    .padding([.leading, .bottom, .trailing], 50)
                                    .background(LinearGradient(
                                        gradient: Gradient(colors: [Color.blue, Color.blue.opacity(0.1)]),
                                        startPoint: .bottom,
                                        endPoint: .top
                                    ))
                                }
                            )
                            
                            .cornerRadius(10)
                            .shadow(radius: 5)
                            
                    case .failure:
                        Image(systemName: "play.display")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 50, height: 50)
                            .padding()
                            .background(Color.gray.opacity(0.3))
                            .cornerRadius(10)
                            .frame(width: customSize.width,
                                   height: customSize.height)
                    @unknown default:
                        EmptyView()
                    }
                }
            }
        }
        .onAppear(perform: {
            customSize = reader.size
        })
        .frame(width: customSize.width, height: customSize.height)
        .background(Color.black.opacity(0.2))
        .cornerRadius(10)
        .shadow(radius: 5)
        .onAppear {
            print("width: \(reader.size.width)")
            print("height: \(reader.size.height)")
        }
        .onChange(of: reader.size) { oldValue, newValue in
            customSize = newValue
        }
    }

    // Function to format the title
    func formatTitle(_ title: String) -> String {
        var formattedTitle = title
        
        // Remove "4K"
        formattedTitle = formattedTitle.replacingOccurrences(of: "4K", with: "", options: .caseInsensitive)
        
        // Remove year in parentheses, e.g., "(2021)"
        let regex = try! NSRegularExpression(pattern: "\\(\\d{4}\\)", options: [])
        formattedTitle = regex.stringByReplacingMatches(in: formattedTitle, options: [], range: NSRange(location: 0, length: formattedTitle.count), withTemplate: "")
        
        // Remove any extra spaces
        formattedTitle = formattedTitle.trimmingCharacters(in: .whitespacesAndNewlines)
        
        return formattedTitle
    }
}

// Extension to apply corner radius to specific corners
extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners))
    }
}

struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(
            roundedRect: rect,
            byRoundingCorners: corners,
            cornerRadii: CGSize(width: radius, height: radius)
        )
        return Path(path.cgPath)
    }
}

image

>Solution :

Instead of using stacks and spacers to align things, you can just pass an alignment to the overlay modifier. The Text will be automatically put on the bottom centre of the image.

Then all you need to do is add some vertical padding on the Text, and do .frame(maxWidth: .infinity) so that it occupies the full width of the image. Finally, you add the gradient background and the text will be centered inside that.

Image("some image")
    .resizable()
    .scaledToFit()
    .overlay(alignment: .bottom) {
        Text("The Instigators")
            .font(.title3.bold())
            .foregroundStyle(.white)
            .lineLimit(1)
            .shadow(radius: 5)
            .padding(.vertical, 20)
            .frame(maxWidth: .infinity)
            .background(.linearGradient(
                colors: [.blue, .blue.opacity(0.1)],
                startPoint: .bottom,
                endPoint: .top
            ))
    }
    .padding()

I see that you are using a lot of deprecated view modifiers. In case you need to support versions below iOS 15, the alignment: argument for overlay is the second argument before iOS 15.

.overlay(
    Text("The Instigators")
        .font(.title3.bold())
        .foregroundColor(.white)
        // ...
        ,
    alignment: .bottom
)

Last but not least, consider replacing the if let statement – you can just do:

Text(formatTitle(channel.tvgName ?? channel.tvgId ?? ""))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading