SwiftUI align navigationBarItems buttons with large navigationBarTitle like in "Messages" - ios

I am creating a SwiftUI app and I am using NavigationView and a large title bar. However, I don't like that navigationBarItems buttons are not aligned with the large title bar (3rd picture) like in the Messages app (first and second picture). I tried to reposition the button, but it wasn't clickable anymore. Does anybody have an idea how to solve this? Thanks!
2nd:
3rd:

Solution: (found here: https://www.hackingwithswift.com/forums/swiftui/icons-in-navigationview-title-apple-messages-style/592)
import SwiftUI
struct ContentView: View {
#State private var midY: CGFloat = 0.0
#State private var headerText = "Contacts"
var body: some View {
NavigationView {
List {
HStack {
//text
HeaderView(headerText: self.headerText, midY: $midY)
.frame(height: 40, alignment: .leading)
.padding(.top, 5)
.offset(x: -45)
HStack {
//button 1
Button(action: {
self.action1()
}) {
Image(systemName: "ellipsis.circle")
.font(.largeTitle)
}
//button 2
Button(action: {
self.action2()
}) {
Image(systemName: "pencil.circle")
.font(.largeTitle)
}
}.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 16))
.foregroundColor(.blue)
} .frame(height: 40, alignment: .leading)
.opacity(self.midY < 70 ? 0.0 : 1.0)
.frame(alignment: .bottom)
ForEach(0..<100){ count in
Text("Row \(count)")
}
}
.navigationBarTitle(self.midY < 70 ? Text(self.headerText) : Text(""), displayMode: .inline)
.navigationBarItems(trailing: self.midY < 70 ? HStack {
//button 1
Button(action: {
self.action1()
}) {
Image(systemName: "ellipsis.circle")
.frame(width: 20, height: 20)
}
//button 2
Button(action: {
self.action2()
}) {
Image(systemName: "pencil.circle")
.frame(width: 20, height: 20)
}
}
:
HStack {
//button 1
Button(action: {
self.action1()
}) {
Image(systemName: "ellipsis.circle")
.frame(width: 0, height: 0)
}
//button 2
Button(action: {
self.action2()
}) {
Image(systemName: "pencil.circle")
.frame(width: 0, height: 0)
}
}
)
}
}
func action1() {
print("do action 1...")
}
func action2() {
print("do action 2...")
}
}
struct HeaderView: View {
let headerText: String
#Binding var midY: CGFloat
var body: some View {
GeometryReader { geometry -> Text in
let frame = geometry.frame(in: CoordinateSpace.global)
withAnimation(.easeIn(duration: 0.25)) {
DispatchQueue.main.async {
self.midY = frame.midY
}
}
return Text(self.headerText)
.bold()
.font(.largeTitle)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

.navigationBarItems(leading:
Button(action: goBack) { HStack { Image("back") } })
.navigationBarItems(trailing: HStack {
Button(action: { self.share() }) { Image("share") }
Button(action: { self.wishlist() }) { Image("wishlist_detail") }})

Related

Change button text on last image of Slider in SwiftUI

I have an image slider with dot indicator named "Onboardingslider". I have used it in my another screen "onboard4" , I want to change the text of "next" button to "get started" only on last image of slider otherwise it stays "next".
I have tried a lot of things but nothing works
Please help me I'm a newbie
struct onboard4: View {
#State var showModal = false
#State var maxlogoheight: CGFloat = 0
#State var isLinkActive = false
var body: some View {
NavigationView {
ZStack{
//max height will be width of the screen
GeometryReader{ proxy -> AnyView in
let height = proxy.frame(in: .global).height
DispatchQueue.main.async {
if maxlogoheight == 0 {
maxlogoheight = height
}
}
return AnyView (
ZStack{
Image("Logo")
.resizable()
.scaledToFit()
.offset(x: getReact().width/3.5, y: -height/1.25)
}//zstack 2
// .padding(.leading,10)
)//anyview
}//end of gr
.frame(maxHeight: getReact().width)
VStack{
Onboardingslider()
Button(action: {
showModal = true
}) {
ZStack{
Text("Next")
.foregroundColor(Color.white)
.fontWeight(.bold)
.frame (width: 295, height: 30)
.padding()
.background(Color("YellowAccent"))
.cornerRadius(20)
.shadow(color: .gray, radius: 5, x: 0, y: 4)
Image("NextButtonOnboard")
}
}
.fullScreenCover(isPresented: $showModal) {
LoginView() }
.offset(y: getSafearea().bottom + -55)
}//vstack
Button(action: {}, label: {
Text("Continue to the listing service")
.underline()
})
.foregroundColor(Color.black)
.offset(y: getSafearea().bottom + 310)
}//zstack
.background(
NavigationLink(destination: LoginView(), isActive: $isLinkActive) {
EmptyView()
}
.hidden()
)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showModal = true
}) {
Text("Skip").underline()
}
.foregroundColor(Color("YellowAccent"))
.font(.system(size: 20,weight: .semibold,design: .serif))
.frame(width: 100, height: 100)
.padding(.top)
.fullScreenCover(isPresented: $showModal) {
LoginView() }
//.padding(.bottom)
}//toolbaritem
}//toolbar
}
}
}
struct onboard4_Previews: PreviewProvider {
static var previews: some View {
onboard4()
}
}
struct Onboardingslider: View {
private let images = ["1", "2", "3", "4"]
init() {
// modify appearance
UIPageControl.appearance().currentPageIndicatorTintColor = .orange
UIPageControl.appearance().pageIndicatorTintColor = .gray
}
var body: some View {
TabView {
ForEach(images, id: \.self) { item in
Image(item)
.padding(.leading,24)
}
}
.frame(height: 600)
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
}
}

How can I add a badge to a leading navigationBarItems in SwiftUI and iOS 14?

How can I add a badge to navigationBarItems in SwiftUI and iOS 14?
I cannot find anything on the net...
I want for example add a badge over the leading navigationBarItems:
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
Text("Peanut")
.padding(-10)
.navigationBarTitle(Text("HomeTitle"), displayMode: .inline)
.navigationBarItems(leading:
HStack {
NavigationLink(destination: Notifications()) {
Image(systemName: "bell")
.font(.system(size: 20))
}.foregroundColor(.white)
}, trailing:
HStack {
NavigationLink(destination: Settings()) {
Image(systemName: "gearshape")
.font(.system(size: 20))
}.foregroundColor(.white)
})
}
}
}
}
You can create a custom Badge view:
struct Badge: View {
let count: Int
var body: some View {
ZStack(alignment: .topTrailing) {
Color.clear
Text(String(count))
.font(.system(size: 16))
.padding(5)
.background(Color.red)
.clipShape(Circle())
// custom positioning in the top-right corner
.alignmentGuide(.top) { $0[.bottom] }
.alignmentGuide(.trailing) { $0[.trailing] - $0.width * 0.25 }
}
}
}
and use it as an overlay:
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
VStack(spacing: 0) {
Text("Peanut")
.padding(-10)
.navigationBarTitle(Text("HomeTitle"), displayMode: .inline)
.navigationBarItems(leading: leadingBarItems)
}
}
}
}
var leadingBarItems: some View {
NavigationLink(destination: Text("Notifications")) {
Image(systemName: "bell")
.font(.system(size: 20))
}
.foregroundColor(.primary)
.padding(5)
.overlay(Badge(count: 3))
}
}
Note
The badge view uses alignment guides for positioning. For more information see:
Alignment Guides in SwiftUI
Here's another example of custom badge
struct BadgeViewModifier: ViewModifier {
let text: String?
func body(content: Content) -> some View {
content
.overlay(alignment: .topTrailing) {
text.map { value in
Text(value)
.fixedSize(horizontal: true, vertical: false)
.font(.system(size: 14, weight: .semibold))
.foregroundColor(DS.Colors.white)
.padding(.horizontal, value.count == 1 ? 2 : 6)
.padding(.vertical, 2)
.background(
Capsule()
.fill(DS.Colors.red)
.if(value.count == 1) { $0.aspectRatio(1, contentMode: .fill) }
)
}
}
}
}
extension View {
func badge(value: String?) -> some View {
modifier(BadgeViewModifier(text: value))
}
#ViewBuilder func `if`<Result: View>(_ condition: Bool, closure: #escaping (Self) -> Result) -> some View {
if condition {
closure(self)
} else {
self
}
}
}

SwiftUI Form Icons

Sorry if this is a really stupid question and maybe offtopic, but I can't seem to find it anywhere.
I'm trying to do a simple settings section for my app and would like to add icons to my elements so users can easily understand what does each setting does. To achieve that, I used Horizontal Stacks (HStack) with an Image and a Label (Text).
This somehow does the trick but I'd like to know if there's a cleaner way to do this.
I'd also like to know how to adjust the separator between cells to stop on the label, and not continue until the end of the element.
As I'm not really good at explaining, here you have two images:
This is what I got
I'd like to have something similar to this
My code:
SettingsView.swift
struct SettingsView: View {
#State var age: Int = 0
var body: some View {
UITableView.appearance().separatorStyle = .singleLine
UINavigationBar.appearance().shadowImage = UIImage()
return NavigationView {
Form {
Section {
Picker(selection: .constant(1), label: HStack {
Image(systemName: "paintbrush")
.font(Font.system(size: 25, weight: .light))
Text("Editor Theme")
}) {
Text("Ayu Mirage").tag(1)
Text("Ayu Light").tag(2)
}
Stepper(value: self.$age,
in: 18...100,
label: {
HStack {
Image(systemName: "rectangle.stack")
.font(Font.system(size: 25, weight: .light))
Text("Number of snippets: \(self.age)")
}
})
NavigationLink(destination: NotificationSettingsView()) {
HStack {
Image(systemName: "app.badge")
.font(Font.system(size: 25, weight: .light))
Text("Notifications")
}
}
}
Section {
VStack(alignment: .leading, spacing: 5) {
Text("Mercury v1.0")
.font(.headline)
Text("Designed with ❤️ by amodrono")
.font(.footnote)
}.padding(.vertical, 5)
}
}
.navigationBarTitle("Hello")
.environment(\.horizontalSizeClass, .regular)
.navigationBarTitle(Text("Settings"), displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
}) {
Image(systemName: "xmark")
.font(.headline)
.imageScale(.large)
.foregroundColor(Color(UIColor(named: "adaptiveColor")!))
}
))
}
}
}
You can disable the separator from the tableview and add your own divider.
Something like this:
struct ContentView: View {
var body: some View {
UITableView.appearance().separatorStyle = .none
return NavigationView {
Form {
Section {
RowView(iconName:"rectangle.stack", text:"Some text")
RowView(iconName:"paintbrush", text:"Some other text", showDivider: false)
}
}
}
}
}
struct RowView: View {
var iconName: String
var text: String
var showDivider = true
var body: some View {
HStack(alignment: .firstTextBaseline) {
Image(systemName: iconName)
VStack(alignment: .leading) {
Text(text)
if showDivider {
Divider()
} else {
Spacer()
}
}
}
}
}
do you mean like this? (if yes, just switch darkmode on ;))
import SwiftUI
#available(iOS 13.0, *)
public struct DarkView<Content> : View where Content : View {
var darkContent: Content
var on: Bool
public init(_ on: Bool, #ViewBuilder content: () -> Content) {
self.darkContent = content()
self.on = on
}
public var body: some View {
ZStack {
if on {
Spacer()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color.black)
.edgesIgnoringSafeArea(.all)
darkContent.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity).background(Color.black).colorScheme(.dark)
} else {
darkContent
}
}
}
}
#available(iOS 13.0, *)
extension View {
#available(iOS 13.0, *)
public func darkModeFix(_ on: Bool = true) -> DarkView<Self> {
DarkView(on) {
self
}
}
}
struct ContentView: View {
#State var age: Int = 0
var body: some View {
UITableView.appearance().separatorStyle = .singleLine
UINavigationBar.appearance().shadowImage = UIImage()
return NavigationView {
Form {
Section {
Picker(selection: .constant(1), label: HStack {
Image(systemName: "paintbrush")
.font(Font.system(size: 25, weight: .light))
Text("Editor Theme")
}) {
Text("Ayu Mirage").tag(1)
Text("Ayu Light").tag(2)
}
Stepper(value: self.$age,
in: 18...100,
label: {
HStack {
Image(systemName: "rectangle.stack")
.font(Font.system(size: 25, weight: .light))
Text("Number of snippets: \(self.age)")
}
})
// NavigationLink(destination: NotificationSettingsView()) {
// HStack {
// Image(systemName: "app.badge")
// .font(Font.system(size: 25, weight: .light))
//
// Text("Notifications")
// }
// }
}
Section {
VStack(alignment: .leading, spacing: 5) {
Text("Mercury v1.0")
.font(.headline)
Text("Designed with ❤️ by amodrono")
.font(.footnote)
}.padding(.vertical, 5)
}
}
.navigationBarTitle("Hello")
.environment(\.horizontalSizeClass, .regular)
.navigationBarTitle(Text("Settings"), displayMode: .inline)
.navigationBarItems(leading: (
Button(action: {
}) {
Image(systemName: "xmark")
.font(.headline)
.imageScale(.large)
// .foregroundColor(Color(UIColor(named: "adaptiveColor")!))
}
))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environment(\.colorScheme, .dark)
.darkModeFix()
}
}

SwiftUI strange behavior in List and NavigationView - Blur function is changing y offset

I've a List of object, in NavigationView, If I blur it, the view is changing the offset, seems it force edgesIgnoringSafeArea at list view:
case 1: If I blur it, the view is changing the offset.
case 2: If I put something before List in VStack, the offset is right (with the space of the object)
case 3: the workaround I found, is to add offset y:1 at the blur effect
struct Test: View {
#State private var arr = [1,2,3,4]
var body: some View {
TabView {
NavigationView {
HStack {
VStack {
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
}
VStack {
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
.blur(radius: 3)
}
}
.navigationBarTitle(Text("TITLE"), displayMode: .inline)
}
.navigationViewStyle(DoubleColumnNavigationViewStyle())//DoubleColumnNavigationViewStyle())
.tabItem {
Image(systemName: "message")
Text("LIST + LIST BLUR")
}
.tag(1)
NavigationView {
HStack {
VStack {
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
}
VStack {
Text("2").frame(width: 0, height: 0)
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
.blur(radius: 3)
}
}
.navigationBarTitle(Text("TITLE"), displayMode: .inline)
}
.tabItem {
VStack {
Image(systemName: "circle")
Text("LIST+VSTACKBLUR")
}
}
.tag(2)
NavigationView {
HStack {
VStack {
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
}
VStack {
List {
ForEach (arr, id:\.self) { _ in
Image(systemName: "person")
.resizable()
.frame(width: 150, height: 150)
}
}
.blur(radius: 3).offset(y:1)
}
}
.navigationBarTitle(Text("TITLE"), displayMode: .inline)
}
.tabItem {
VStack {
Image(systemName: "circle")
Text("List+BLUR+OFFSET")
}
}
}
}
}
struct Test_Previews: PreviewProvider {
static var previews: some View {
Test()
}
}
UPDATE:
I can't blur inside list because I'm using an extension of View to show personal TextFieldAlert
extension View {
func textFieldAlert(isShowing: Binding<Bool>,
text: Binding<String>,
title: String, grayText: String, onReturnPressed: #escaping () -> Void) -> some View {
TextFieldAlert(isShowing: isShowing,
text: text,
presenting: { self },
title: title, grayText: grayText, onReturnPressed:
onReturnPressed)
}
}
struct TextFieldAlert<Presenting>: View where Presenting: View {
#Binding var isShowing: Bool
#Binding var text: String
let presenting: () -> Presenting
let title: String
var grayText:String
let onReturnPressed: () -> Void
func returnFunction() {//quando l'utenti schiaccia ok o invio}
self.isShowing = false
self.onReturnPressed()
}
var body: some View {
GeometryReader { (deviceSize: GeometryProxy) in
ZStack {
if self.isShowing {
self.presenting()
.blur(radius: 3).offset(y:1) //I've added this
.disabled(self.isShowing)
}
else {
self.presenting()
.disabled(self.isShowing)
}
VStack {
Spacer()
.frame(height: 8)
Text(self.title)
Divider()
TextField(self.grayText, text: self.$text, onCommit: {
self.returnFunction()
})
Divider()
Spacer()
.frame(height: 10)
HStack {
Button(action: {
withAnimation {
self.returnFunction()
}
}) {
Text("OK")
.frame(width: 150, height: 30)
.background(Color.white)
//.foregroundColor(.white)
.cornerRadius(15)
}
}
}
.padding()
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 15))
.frame(
width: 250,//deviceSize.size.width*0.6,
height: 220//deviceSize.size.height*0.6
)
.offset(y: -100)
.shadow(radius: 20)
.opacity(self.isShowing ? 1 : 0)
ZStack {
Circle().frame(width: 40, height: 40)
.foregroundColor(Color.white)
Image(systemName: "pencil.circle.fill")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 50, height: 50)
.clipShape(Circle())
.foregroundColor(Color.blue)
}
.offset(x:0, y: -220/3 - 110)//-deviceSize.size.height*0.6/6 - 110)
.opacity(self.isShowing ? 1 : 0)
}
}
}
}

Buttons inside list item don't work properly

I have 2 views: PollCard and PollList (like list of polls)
In the PollCard view I have 2 buttons(images), that calls "answer" function:
HStack{
Button(action: {
self.answer()
print("Pressed first image")
}){
Image(poll.v1img)
.resizable()
.renderingMode(.original)
.scaledToFill()
.frame(width: 150, height: 200)
}.frame(width: 150, height: 200)
Button(action: { self.answer()}){
Image(poll.v2img )
.resizable()
.renderingMode(.original)
.frame(width: 150, height: 200)
}.frame(width: 150, height: 200).zIndex(4)
}
In the PollList view I have this simple list:
var body: some View {
HStack{
List(pollData) { poll in
PollCard(poll: poll)
}.padding()
}
}
But when I click the images in the list, it selects like all images and presses it
It is also very easy to check - terminal prints Pressed first image even if I've pressed only second image
What should I do to fix this?
As I mentioned in the comment section the workaround would be to substitute the HStack around the List with a ScrollView and the List with a ForEach:
struct ContentView: View {
struct Data: Identifiable {
var id: Int
}
#State var data = [Data(id: 0), Data(id: 1), Data(id: 2), Data(id: 3), Data(id: 4), Data(id: 5)]
var body: some View {
ScrollView {
ForEach(self.data) { data in
HStack {
Button(action: {
print("Pressed blue...")
}, label: {
Rectangle()
.foregroundColor(Color.blue)
.frame(width: 150, height: 200)
})
Button(action: {
print("Pressed red...")
}, label: {
Rectangle()
.foregroundColor(Color.red)
.frame(width: 150, height: 200)
})
}
}
}
}
}
I hope this helps!
this is an unexpected behavior of Button (or it is a bug?) in current SwiftUI (either on macOS or iOS).
The workaround in your case is simple, try to apply PlainButtonStyle for your buttons
import SwiftUI
struct ContentView: View {
var body: some View {
List {
Text("Hello, World!")
HStack {
Button(action: {
print("button1")
}) {
Color.yellow
}.buttonStyle(PlainButtonStyle())
Button(action: {
print("button2")
}) {
Color.green
}.buttonStyle(PlainButtonStyle())
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The funny thing is that it is enough to apply the style on one button only ... or apply it to parent HStack :-)
changing ContentView ...
struct ContentView: View {
var body: some View {
List {
Text("Hello, World!")
HStack {
Button(action: {
print("button1")
}) {
Color.yellow
}
Button(action: {
print("button2")
}) {
Color.green
}
}
.buttonStyle(PlainButtonStyle())
.frame(height: 100)
Text("By by, World!")
}
}
}
you get
where each of buttons works as expected

Resources