The element inside of the SwiftUIPager is hidden when the screen border is touched - swift

In my code I have HStack with Buttons inside of Pager. Button's label is Text view with RoundedRectangle as it's overlay.
Pager(page: vm.selectedWeekPage, data: vm.deltaWeeksArray, id: \.self) { deltaWeek in
HStack{
ForEach (vm.fetchWeek(vm.deltaWeek(delta: deltaWeek)), id: \.self){
day in
Button {
vm.selectedDay = day
} label: {
Text("\(vm.extractDate(date: day, format: "dd"))")
.font(.custom("Unbounded", size: 20))
.fontWeight(.bold)
.foregroundColor(Color(tm.getTheme().foregroundColor))
.frame(width: 42, height: 42)
.overlay(content: {
RoundedRectangle(cornerRadius: 15, style: .continuous)
.stroke(lineWidth: 2)
.foregroundColor(Color(tm.getTheme().foregroundColor))
.frame(width: 46, height: 46)
.fixedSize()
})
.frame(maxWidth: .infinity)
}
}
}
.padding(.horizontal, 18)
}
.singlePagination()
.pagingPriority(.simultaneous)
.onPageChanged({ page in
if page >= vm.deltaWeeksArray.count - 2 {
(1..<4).forEach { _ in
vm.deltaWeeksArray.append((vm.deltaWeeksArray.last ?? 0) + 1)
vm.deltaWeeksArray.removeFirst()
vm.selectedWeekPage.index -= 1
}
} else if page <= 1 {
(1..<4).forEach { _ in
vm.deltaWeeksArray.insert((vm.deltaWeeksArray.last ?? 0) + 1, at: 0)
vm.deltaWeeksArray.removeLast()
vm.selectedWeekPage.index += 1
}
}
})
.itemSpacing(50)
.frame(maxHeight: 48)
.padding(.horizontal, -18)
Bug happens when I transition to different page. .stroke() hides when Button touches the edge of screen.
Recording of bug:
Link
Actually I don't really know what to do... I tried using ZStack instead of .overlay(),extracting text from the label on the button. That didn't work

Related

SwiftUI Centering Image between Views

I have an HStack that I am using as a tool bar. The HStack contains a button on the left, image in the middle, and then another HStack containing 2 buttons. Everything looks fine except for the image in the middle, it's slightly to the left. How can I get the image centered?
var toolbar: some View {
HStack(alignment: .center) {
Button {
UIImpactFeedbackGenerator(style: .light).impactOccurred()
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "xmark")
.tint(.black)
}
Spacer()
Image("nav_logo")
.resizable()
.frame(width: 30, height: 30)
Spacer()
optionsButton
}
.frame(height: 30)
}
var optionsButton: some View {
HStack {
Button {
UIPasteboard.general.string = viewModel.take.shareLinkString
self.buildBottomAlert(type: .linkCopied)
} label: {
Image("shareIcon")
}
.padding(.trailing, 10)
Button {
showingAlert = true
} label: {
Image("moreMenu")
}
}
The image in the middle is offset because the first button and optionsButton do not have the same width. But the SpacerĀ“s inside the containing Stack do have equal width.
To solve this you could wrap them in to a container that is big enough so both have the same width.
e.g.:
//GeometryReader to get the size of the container this is in
GeometryReader{proxy in
HStack(alignment: .center) {
//Hstack to wrap the button
HStack(){
Button {
UIImpactFeedbackGenerator(style: .light).impactOccurred()
presentationMode.wrappedValue.dismiss()
} label: {
Image(systemName: "xmark")
.tint(.black)
}
// Spacer to move the button to the left
Spacer()
}
// depending on the use case you would need to tweak this value
.frame(width: proxy.size.width / 4)
Spacer()
Image(systemName: "square.and.arrow.up.trianglebadge.exclamationmark")
.resizable()
.frame(width: 30, height: 30)
Spacer()
HStack(){
Spacer()
optionsButton
}
.frame(width: proxy.size.width / 4)
}
.frame(height: 30)
}

SwiftUI keyboard navigation in lists on MacOS

I'm trying to implement a list which can be navigated with arrow keys - up/down. I've created layout, but now I don't totally understand how(and where) to make up/down keys intercepted so I could add my custom logic. I already tried onMoveCommand with focusable but that did not work(wasn't firing at all)
Code I have - below
public var body: some View {
VStack(spacing: 0.0) {
VStack {
HStack(alignment: .center, spacing: 0) {
Image(systemName: "command")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.leading, 20)
.offset(x: 0, y: 1)
TextField("Search Commands", text: $state.commandQuery)
.font(.system(size: 20, weight: .light, design: .default))
.textFieldStyle(.plain)
.onReceive(
state.$commandQuery
.debounce(for: .seconds(0.1), scheduler: DispatchQueue.main)
) { val in
state.fetchMatchingCommands(val: val)
}
.padding(16)
.foregroundColor(Color(.systemGray).opacity(0.85))
.background(EffectView(.sidebar, blendingMode: .behindWindow))
}
}
Divider()
VStack(spacing: 0) {
List(state.filteredCommands.isEmpty && state.commandQuery.isEmpty ?
commandManager.commands : state.filteredCommands, selection: $selectedItem) { command in
VStack(alignment: .leading, spacing: 0) {
Text(command.title).foregroundColor(Color.white)
.padding(EdgeInsets.init(top: 0, leading: 10, bottom: 0, trailing: 0))
.frame(height: 10)
}.frame(maxWidth: .infinity, maxHeight: 15, alignment: .leading)
.listRowBackground(self.selectedItem == command ?
RoundedRectangle(cornerRadius: 5, style: .continuous)
.fill(Color(.systemBlue)) :
RoundedRectangle(cornerRadius: 5, style: .continuous)
.fill(Color.clear) )
.onTapGesture {
self.selectedItem = command
callHandler(command: command)
}.onHover(perform: { _ in self.selectedItem = command })
}.listStyle(SidebarListStyle())
}
}
.background(EffectView(.sidebar, blendingMode: .behindWindow))
.foregroundColor(.gray)
.edgesIgnoringSafeArea(.vertical)
.frame(minWidth: 600,
minHeight: self.state.isShowingCommandsList ? 400 : 28,
maxHeight: self.state.isShowingCommandsList ? .infinity : 28)
}
This is how it looks - and I want to make focus move between found list items
If I understand your question correctly, you want to use the arrow keys to "move" from the search TextField, to the list of items, and then navigate the list with the up/down arrow keys.
Try something simple like this example code, to monitor the up/down arrow keys, and take the appropriate action.
Adjust/tweak the logic to suit your needs.
import Foundation
import SwiftUI
import AppKit
struct ContentView: View {
let fruits = ["apples", "pears", "bananas", "apricot", "oranges"]
#State var selection: Int?
#State var search = ""
var body: some View {
VStack {
VStack {
HStack(alignment: .center, spacing: 0) {
Image(systemName: "command")
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.padding(.leading, 20)
.offset(x: 0, y: 1)
TextField("Search", text: $search)
.font(.system(size: 20, weight: .light, design: .default))
.textFieldStyle(.plain)
}
}
Divider()
List(selection: $selection) {
ForEach(fruits.indices, id: \.self) { index in
Text(fruits[index]).tag(index)
}
}
.listStyle(.bordered(alternatesRowBackgrounds: true))
}
.onAppear {
NSEvent.addLocalMonitorForEvents(matching: [.keyDown]) { nsevent in
if selection != nil {
if nsevent.keyCode == 125 { // arrow down
selection = selection! < fruits.count ? selection! + 1 : 0
} else {
if nsevent.keyCode == 126 { // arrow up
selection = selection! > 1 ? selection! - 1 : 0
}
}
} else {
selection = 0
}
return nsevent
}
}
}
}

Is there a way to have lazy pickers similar to a lazyvstack?

Currently, I have a popup menu that contains two wheel pickers. I have create a drag gesture to allow swiping the popup menu away. However, as I add more entries to the picker wheels, the drag gesture becomes more and more laggy. The animation is fluid if I remove one of the pickers, or if I have less entries. How can I keep the animation fluid while having a lot of entries in both pickers? I have tried using a drawingGroup, however it doesn't work with a picker. Is there something along the lines of lazy loading for pickers?
Here are the pickers on the popup:
GeometryReader { geo in
HStack(spacing: 0) {
Picker(selection: $selection1) {
ForEach(0 ..< array1.count) { index in
...
}
} label: {
}
.labelsHidden()
.pickerStyle(.wheel)
.frame(width: geo.size.width / 2)
.compositingGroup()
.clipped()
Picker(selection: $selection2) {
ForEach(0 ..< array2.count) { index in
...
}
} label: {
}
.labelsHidden()
.pickerStyle(.wheel)
.frame(width: geo.size.width / 2)
.compositingGroup()
.clipped()
}
}
Here is the popup menu containing the pickers:
VStack {
ZStack {
ZStack {
Color("whiteblack")
VStack {
Capsule()
.frame(width: 40, height: 5)
.foregroundColor(.gray)
.padding(.top, 5)
Spacer()
}
}
.gesture(DragGesture(minimumDistance: 0, coordinateSpace: .global)
.onChanged({ value in
let amt = prevDragHeight - value.translation.height
if slideOffset >= 0 {
slideOffset -= amt
}
if slideOffset < 0 {
slideOffset = 0
}
prevDragHeight = value.translation.height
})
.onEnded({ _ in
if slideOffset > 70 {
withAnimation(.interactiveSpring()) {
slideOffset = 0
isShowing = false
}
slideOffset = 0
} else {
withAnimation(.interactiveSpring()) {
slideOffset = 0
}
}
prevDragHeight = 0
})
)
DoublePicker()
}
}
.frame(height: 400)
.frame(maxWidth: .infinity)
.background(.white)
.cornerRadius(20)
.padding()
.padding(.bottom)
.offset(x: 0, y: slideOffset)
.transition(.move(edge: .bottom))

SwiftUI - NavigationLink nested within a button

I'm working on a onboarding screen:
VStack {
if #available(iOS 14.0, *) {
TabView(selection: $currentStep) {
ForEach(0..<OnBoardingSteps.count) { it in
VStack {
Text(OnBoardingSteps[it].title)
Image(OnBoardingSteps[it].image).resizable().frame(width: 200, height: 200)
Text(OnBoardingSteps[it].description)
}.tag(it)
}
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
} else {
// Fallback on earlier versions
}
HStack {
Spacer()
Spacer()
HStack{
ForEach(0..<OnBoardingSteps.count) { it in
if it == currentStep {
Rectangle()
.frame(width: 10, height: 10)
.cornerRadius(10)
.foregroundColor(.purple)
} else {
Circle()
.frame(width: 10, height: 10)
.foregroundColor(.gray)
}
}
}
Spacer()
Button(action: {
if self.currentStep < OnBoardingSteps.count - 1 {
self.currentStep += 1
} else {
// now when i click 'Done' it should take me to Signup screen
}
}){
HStack {
Text(currentStep < OnBoardingSteps.count - 1 ? "Next" : "Done")
.foregroundColor(.blue)
Image(systemName: currentStep < OnBoardingSteps.count - 1 ? "arrow.right" : "checkmark")
}
}
}
}
In my button I have some logic to change text based on if I'm on the last onBoarding step.
What im trying to achieve is, when im on the last step, when i click 'Done' i should navigate to the Signup screen.

Style the picker in SwiftUI

I'm doing a login form in Swiftui, however when i try to add a form that contain picker for age the style get messed up and since im new to SwiftUI i didn't know how can i fix this problem?
How can i fix the picker style so it addapte with the Nickname field?
here it is the code that i tried:
#State var nickName: String = ""
#State var age: Int = 18
var body: some View {
VStack {
HStack {
Image(systemName: "person.circle.fill")
.foregroundColor(Color("ChatiwVeryLightBlue"))
.frame(width: 44, height: 44)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.shadow(color: Color.black.opacity(0.15), radius: 5, x: 0, y: 5)
.padding(.all, 10)
TextField("Nickname", text: $nickName)
.frame(maxWidth: .infinity)
.frame(height: 50)
}
Divider().padding(.leading, 80)
HStack {
Image(systemName: "person.circle.fill")
.foregroundColor(Color("ChatiwVeryLightBlue"))
.frame(width: 44, height: 44)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.shadow(color: Color.black.opacity(0.15), radius: 5, x: 0, y: 5)
.padding(.all, 10)
Form {
Section {
Picker(selection: $age, label: Text("Picker")) {
ForEach(18 ..< 99, id: \.self) { index in
Text("\(index)").tag(1)
}
}
}
}
}
// Age
// Country
// Male/Female
}
.background(BlurView(blurType: .systemMaterial))
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))
.padding()
.offset(y: 400)
Not sure if you are still working on this project, but here is a neat workaround:
struct ContentView: View {
#State private var age = 18
var body: some View {
NavigationView {
VStack {
Divider()
HStack {
NavigationLink(destination: AgePickerView(age: $age)) {
HStack {
Text("Age")
Spacer()
Text("\(age) >")
.padding(.trailing)
}
}
}
.foregroundColor(.gray)
Divider()
}
.padding(.leading, 80)
}
}
struct AgePickerView: View {
#Binding var age: Int
var body: some View {
List(18..<99) { index in
Button(action: {
self.age = index
}) {
HStack {
Text("\(index)")
Spacer()
if index == age {
Image(systemName: "checkmark")
.foregroundColor(.blue)
}
}
}
}
}
}
}
To get the same NavigationView style but remove the Form style, you need to wrap a NavigationLink in a NavigationView.
The little > sign I put at the end of the Text view in the NavigationLink doesn't perfectly match the one in the Form style, so you can look around for an image that is more similar if you would like.
The AgePickerView is pretty straightfoward; it's just a list of buttons that are modifying a binding to the original state variable in ContentView through tap gestures. The checkmark is there to imitate a Picker view.
The best part of this is that you don't have to worry about all that index shifting with a picker.