I've got a ZStack in SwiftUI filled with some components delivered by a ForEach, as follows:
ForEach(0..<arr.count) { i in
ZStack {
...
}
// I use i later in this code
...
}
The program runs perfectly like this.
But I want to add padding to the ZStack only if i == 0, so I tried adding this modifier to the ZStack: .padding(.top, i == 0 ? 70 : 0)
When I try to build it with this modifier it fails, but doesn't even say "build failed." It takes about 5 minutes attempting to build (when it usually takes 5 seconds) then decides to crash. Can anyone explain why this is happening and how I can get this conditional padding without breaking my program?
Try this:
ForEach(0..<arr.count) { i in
ZStack {
...
}
.padding(.top, getPadding(i))
// I use i later in this code
...
}
func getPadding(_ i: Int) -> CGFloat {
if i == 0 {
return CGFloat(70)
}
return CGFloat(0)
}
func setPadding(_ statement: String) -> CGFloat {
if statement == " Put your statement " {
return CGFloat(your requirement padding)
} else {
return CGFloat("According to your requirement padding")
}
Related
This is a problem on iOS 15 RC only. It works all good on iOS 14.
The problem is if my destination on NavigationLink is created dynamically, it pop automatically if i try push a view via NavigationLink. Here is a bit code snippet
NavigationLink(
destination: (createSettingNavigationLink(name: nameAndImage[0])),
label: {
CellButton(title: nameAndImage[0], image: nameAndImage[1])
}
func createSettingNavigationLink(name: String) -> some View {
if name == "shop.Settings" {
return ShopSettings()
}
if name == "shop.Goods" {
return ShopGoodsManagementPage()
}
if name == "shop.Order" {
return ShopOrderPage()
}
if name == "Customer Service" {
return ServiceListPage(isStore: true)
}
if name == "shop.Performance" {
return PerformanceManagementPage()
}
if name == "shop.Earnings" {
return CommissionSummaryPage()
}
if name == "shop.Increase_sales" {
return BCWebView(urlStr: Constants.increaseSalesGuide)
.navigationBarTitle(Text("Increase Sales Guide"), displayMode: .inline)
.navigationBarHidden(false)
)
}
return EmptyView()
}
)
By the way, the approach mentioned here SwiftUI Unexpectedly NavigationLink pops automatically does not help.
My final solution is to change the above function to struct view. I think it makes sense since the struct view seems to keep states while function is pure stateless.
As far as I read about conditional Views this code should work. But it doesn't.
struct Consts {
static let myCondition = false //no difference if true or false
}
struct ContentView: View {
#State var toggle: Bool = false
var body: some View {
List() {
Text("Short Text is in first line")
Text("Second Line Text is a little longer but not much")
if Consts.myCondition {
Text("This is a conditional text. As in: when the user hasn't purchased the option he / she don't need a hint how to use this feature.")
// } else {
// Text("else doesn't help either.")
}
Toggle("I also have a toggle but it has nothing to do with this.", isOn: $toggle)
Text("Here we have a longer Text. Dont know what to type any more.")
Text("More text which is longer than a few lines.")
Text("Not so long Text")
}
.navigationTitle("Hints & Settings")
}
}
It compiles without warnings or errors. It loads up and displays fine, on simulator and on device. But every time I scroll the List upwards from the end, as soon as this if condition { Text() } should become visible the app crashes with
Fatal error: file SwiftUI, line 0
2021-03-07 06:36:26.548126+0100 WTFif WatchKit Extension[23163:641812] Fatal error: file SwiftUI, line 0
This is not limited to watchOS. It reproduces the same way in iOS, just the Texts have to be longer so that the if condition { Text() } becomes invisible when scrolling.
I have worked around the error with an array, conditional ranges and two ForEach() blocks.
struct ContentView: View {
let myHints = ["Short Text is in first line",
"Second Line Text is a little longer but not much",
"This is a conditional text. As in: when the user hasn't purchased the option he / she don't need to hint how to use this feature.",
"Here we have a longer Text. Dont know what to type any more.",
"More text which is longer than a few lines.",
"Not so long Text"]
var myUpperRange: ClosedRange<Int> {
if Consts.myCondition {
return 0...1
} else {
return 0...2
}
}
var myLowerRange: ClosedRange<Int> {
return 3...5
}
#State var toggle: Bool = false
var body: some View {
List() {
ForEach (myUpperRange, id: \.self) { i in
Text(myHints[i])
}
Toggle("I also have a toggle but it has nothing to do with this.", isOn: $toggle)
ForEach (myLowerRange, id: \.self) { i in
Text(myHints[i])
}
}
.navigationTitle("Hints & Settings")
}
}
My question basically is: am I not getting it or did I find a bug in Xcode / SwiftUI? Should my code above work? What could I have done different to make it work with the simple list of Texts?
Background: I also have a TabView with an if condition { MyTab() } which works without crashing. Do I have to worry that this might crash in the same way? Should I work around this as well before shipping?
PS: I am using Xcode 12.4 (12D4e)
Apparently this has been fixed by iOS 15 Beta 4: on my test device I had the error prior to the Beta 4 update, using a test app compiled with Xcode 13 Beta 3. After the update to iOS 15 Beta 4 the error is gone.
So I think it’s reasonable to say that the update to iOS 15 Beta 4 did fix the error.
I have a view that encapsulates some presentation logic, and as part of that logic, it can hide itself. A toy example:
struct Item: View {
var x: Int
var body: some View {
if x % 3 == 1 {
return AnyView(EmptyView())
}
return AnyView(Text("\(x)").background(Color.blue))
}
}
When I'm using my Item's in a VStack, it is smart enough to insert spacing only between non-empty ones.
VStack(spacing: 8) {
Item(x: 0)
Item(x: 1)
Item(x: 2)
Item(x: 3)
}
Now I want to do the same, but using a custom separator instead of spacing. Similarly, I want separator to be inserted only between non-empty items.
Is there an API that would insert 2 separators between 3 visible views? Something like this:
Something(separator: Divider()) {
Item(x: 0)
Item(x: 1)
Item(x: 2)
Item(x: 3)
}
I've checked VStack, Group, ForEach but didn't find anything. I really don't want to lift the hiding logic up into the parent. Any ideas for the workaround that would keep the hiding logic inside the Item?
Here is possible approach (and you continue use same VStack)
Tested with Xcode 12 / iOS 14
struct Item: View {
var x: Int
var body: some View {
if x % 3 == 1 {
return AnyView(EmptyView())
}
return AnyView(
VStack {
Text("\(x)").background(Color.blue)
Divider().padding(.horizontal)
}
)
}
}
I got my data from API and put them in a list. My problem is that is it possible to set fixed height when the array in the list were all deleted? I use environment(.defaultMinListRowHeight, 80) but it didn't work.Please help. Thank you
func containedView() -> AnyView {
switch self.selectedIndex {
case 0:
return AnyView(
List(userNotifications1.userNotificationsArray) { userNotification in
UserNotificationCellView(userNotification: userNotification,userNotifications: self.userNotifications1)
}.environment(\.defaultMinListRowHeight, 80)
.environment(\.defaultMinListHeaderHeight, 10)
)
case 1:
return AnyView(
List {
ForEach(managerNotifications, id: \.id) { (managernotification) in
ManagerNotificationCellView(managerNotification : managernotification)
}.onDelete(perform: self.deleteManagerNotification)
}
)
default:
return AnyView(Text("22").padding(40))
}
when list has data, the cell's height are bigger
when list is empty,the cell's height was small
Make sure to set environment variable in correct place. Below tested as works with Xcode 11.2 / iOS 13.2.
List {
// ... list content here
}
.environment(\.defaultMinListRowHeight, 80) // << should be for List
I'm looking for a way to check the state of the option-key in SwiftUI on macOS.
I.e. depending on whether the option key is pressed or not I want to perform different actions in the .onTapGesture closure.
macOS-only SwiftUI has .modifiers modifier to specify EventModifiers, so your case is covered like in below example:
Rectangle()
.fill(Color.yellow)
.frame(width: 100, height: 40)
.gesture(TapGesture().modifiers(.option).onEnded {
print("Do anyting on OPTION+CLICK")
})
.onTapGesture {
print("Do anyting on CLICK")
}
While using the modifiers method on the gesture should probably be preferred, one can also actually test for the option key itself using CGEventSource in CoreGraphics:
import CoreGraphics
extension CGKeyCode
{
static let kVK_Option : CGKeyCode = 0x3A
static let kVK_RightOption: CGKeyCode = 0x3D
var isPressed: Bool {
CGEventSource.keyState(.combinedSessionState, key: self)
}
static var optionKeyPressed: Bool {
return Self.kVK_Option.isPressed || Self.kVK_RightOption.isPressed
}
}
This lets you detect the option key (or any other keys for that matter) in contexts where there isn't a modifier property or method.
The key codes in the extension can be renamed to be more Swifty, but those are the names that go way back to Classic MacOS's Toolbox and were defined in Inside Macintosh. I have a gist containing all the old key codes.
2022
.onTapGesture {
if NSEvent.modifierFlags.contains(.option) {
print("Option key Tap")
} else {
print("Tap")
}
}
.onDrag {
if NSEvent.modifierFlags.contains(.option) {
return NSItemProvider(object: "\(item.id)" as NSString)
}
return NSItemProvider()
}