Visual Design

RSS for tag

Discuss animation, branding, color, layout, typography, and other elements of great looking apps.

Posts under Visual Design tag

44 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

Xcode Template icon color matching default ones
I want to create a Project Template with an icon that matches the default Xcode templates. I created a vector example icon and saved it as TemplateIcon.svg and it gets displayed for my .xcodetemplate. I would like it to blend in with Apple's default icons (which I know are assets contained inside Xcode bundles). I have tried with fill="currentColor" in my svg xml but it doesn't work. I couldn't find any technical documentation about this. It would be great to know how to design an icon that gets dynamically tinted by Xcode like the default ones. Thanks πŸ™‚
0
0
392
Jun ’24
Compact screens, keyboard and dynamic type UI issues
When testing my app on an iPhone SE i noticed an issue with the UI when editing some text in a text field. When the keyboard comes up it pushes everything in the view up and part of the text field gets covered by the navigation title and buttons. It gets worse when testing dynamic fonts and changing the text to XXLarge or higher. There are no issues on a larger screen. Is it possible to prevent the keyboard from push up the content of the view when it is displayed? This is with the default font of large. This is with the extra large text var body: some View { NavigationStack { VStack { HStack { Text("Name") Spacer() TextField("Name", text: $name) .multilineTextAlignment(.trailing) .textFieldStyle(.roundedBorder) .frame(width: 240) .onChange(of: name) { if name.count >= 10 { isShowingLongNameWarning = true } else { isShowingLongNameWarning = false } } } VStack(alignment: .trailing){ HStack { Text("Short Name") Spacer() TextField("Short Name", text: $shortName.max(shortNameCharacterLimit)) .multilineTextAlignment(.trailing) .textFieldStyle(.roundedBorder) .frame(width: isShowingLongNameWarning ? 215 : 240) if isShowingLongNameWarning { VStack { Image(systemName: "exclamationmark.circle") } .popoverTip(shortNameTip) } } Text("Short Name Length: \(shortName.count) characters") .font(.caption) .foregroundStyle(shortName.count >= shortNameCharacterLimit ? .red : .primary) } HStack { Text("Fluid Amount") Spacer() TextField("Fluid Amount", value: $amount, format: .number) .multilineTextAlignment(.trailing) .textFieldStyle(.roundedBorder) .keyboardType(.decimalPad) .focused($numberPadIsFocused) .frame(width: 140) .toolbar { if numberPadIsFocused { ToolbarItemGroup(placement: .keyboard) { Spacer() Button("Done") { numberPadIsFocused = false } } } } } HStack { Text("Unit of Measure") Spacer() Picker("Unit of measure", selection: $unitOfMeasure) { ForEach(FluidUnit.allCases) { Text($0.title) } } .tint(colorScheme == .dark ? .yellow : .pink) } .accessibilityElement() Toggle("Favorite Drink", isOn: $isFavorite) Text("Drink Image") ScrollView(.horizontal) { HStack{ ForEach(DataStore.drinkImages, id: \.self) { image in Button { selectDrinkImage(imageName: image) } label: { ZStack { Image(image) .resizable() .scaledToFit() .frame(height: 100) if imageName == image { SelectedDrinkImageView() } } } .padding(.trailing, 30) } } .scrollTargetLayout() } .scrollIndicators(.hidden) .scrollTargetBehavior(.viewAligned) Spacer() .navigationTitle("Add a Drink") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .topBarTrailing) { Button("Save") { addDrink() } .tint(colorScheme == .dark ? .yellow : .pink) .disabled(disableSaveButton()) } ToolbarItem(placement: .topBarLeading) { Button("Cancel") { dismiss() } .tint(colorScheme == .dark ? .yellow : .pink) } } } .padding() } }
0
0
402
Jun ’24
Force accessible Color
Looking at Apple's Color Guidelines and taking into account that building my app to work for both light/dark modes I run into an issue where I want to use colors for statuses: red and green. I want to avoid declaring custom colors and I try to use the default ones that adjust for the selected appearance. Unfortunately I noticed that the Color.green does not work well on the default light background of my view (.background(Color (.windowBackgroundColor))) because there is not enough contrast. I noticed that Apple also lists "Accessible" versions of those colors but these are probably managed dynamically. My question: Is there a way to programatically force my app to use the accessible version of a Color (e.g. green)? Thanks
1
0
448
Jun ’24
How to interact during the Transition process?(wwdc24 UIKit consultation)
Recently I tried to apply a custom transition to a custom contextMenu. However, I want to make sure that during the transition process (which is not over yet), my contextMenu elements such as buttons can be tapped. But I tried a lot of things without success. I know you have a lot of experience, so I would like to ask you about how to implement the transition and be able to interact before it is over. I know that a UIView can be tapped during animation, but I haven't tried the button in a UIView. I've been trying to transition ViewControllers. For example, in a transition from fromViewController to toViewController, I wanted to be able to tap on a tableView in toViewController during the transition, but I was frustrated and found it very difficult to implement. I would like to ask you about the possibilities of interaction during the Viewcontrollers transition. (PS: In Github Issues, I uploaded a GIF example of the plus button on the left of the input box in iMessage. After tapping the plus button, you can tap the "Apple Cash" button before the transition is finished.) Your advice would be incredibly valuable to me. Thank you in advance for your time and assistance. [GithubLink]https://github.com/Juhnkerg/DemoForInteractionDuringTransition)
0
1
476
Jun ’24
Swift Charts animation woes using centered AxisValueLabel
Hi, I'm having some trouble when animating my chart with a custom AxisValueLabel. Specifically, as soon as I set its init parameter centered to true, the x axis' leftmost value of the previous dataset sticks around during the animation to the next dataset. Here's a GIF of a screen recording from a minimum reproducible example I built. Keep a close eye on the x axis of the third BarMark, and notice how the 0 from the first BarMark sticks around longer than necessary / intended. While it isn't visible in the GIF, the extra 0 eventually does disappear, but only after the transition if fully complete, making the animation feel distracting and amateur-ish, rather than smooth. This is my code for the x axis. If I turn centered to false, this problem immediately goes away. .chartXAxis { AxisMarks( preset: .aligned, values: .stride( by: .day, count: 1, calendar: .current ) ) { value in AxisValueLabel(centered: true) { Text("\(value.index)") } } } As you might be able to tell, my x axis is date based, and I'm working on showing one BarMark per day of the week. I have a ZIP of my minimum reproducible example that I can provide for anyone interested, although I don't know how to share it here. Any advice on what I can do to fix this?
2
0
498
Jun ’24
How to replicate UIImageView's `scaleAspectFill` for Images inside a custom Layout?
I've defined a custom layout container by having a struct conform to Layout and implementing the appropriate methods, and it works as expected. The problem comes when trying to display Image, as they are shown squished when just using the .resizable() view modifier, not filling the container when using .scaledToFit() or extending outside of the expected bounds when using .scaledToFill(). I understand that this is the intended behaviour of these modifiers, but I would like to replicate UIImageView's scaledAspectFill. I know that this can usually be achieved by doing: Image("adriana-wide") .resizable() .scaledToFill() .frame(width: 200, height: 200) .clipped() But hardcoding the frame like that defeats the purpose of using the Layout, plus I wouldn't have direct access to the correct sizes, either. The only way I've managed to make it work is by having a GeometryReader per image, so that the expected frame size is known and can bet set, but this is cumbersome and not really reusable. GalleryGridLayout() { GeometryReader { geometry in Image("adriana-wide") .resizable() .scaledToFill() .frame(width: geometry.size.width, height: geometry.size.height) .clipped() } [...] } Is there a more elegant, and presumably efficient as well as reusable, way of achieving the desired behaviour? Here's the code of the Layout.
4
0
720
Jul ’24
Best Way to Support Different Devices in SwiftUI?
Hi, I have pretty much finished my app's layout but realized I needed to scale it for different devices. I have read online that hardcoding values (esp in frames) is a big no-no, and GeometryReader should be heavily utilized. Also was recommended ViewThatFits. The problem is, I want the app to look the exact same across all devices. What is the best way to get started? Also, when testing, do I only have to test on an iPad and iPhone or are the dimensions significantly different amongst each class of devices?
0
0
422
Apr ’24
Animates wrongly at every switch of direction, animates right in same direction
I need to create an animation for previous and next questions. The current question should move out and next/prev question should come in. If you run the app and keep pressing next, it works well. Current question moves out towards left Next question appears from right to left. But if I switch the direction to previous, Current question moves out towards left( should move out towards right) next question comes in from left to right( which is correct). Similar discrepancy happens when I switch direction from prev to next. Why is this so? import SwiftUI struct Question { let id: Int let text: String } extension AnyTransition { static var slideRight: AnyTransition { let insertion = AnyTransition.move(edge: .trailing) let removal = AnyTransition.move(edge: .leading) return .asymmetric(insertion: insertion, removal: removal) } static var slideLeft: AnyTransition { let insertion = AnyTransition.move(edge: .leading) let removal = AnyTransition.move(edge: .trailing) return .asymmetric(insertion: insertion, removal: removal) } } struct QuizView: View { let questions = [ Question(id: 1, text: "11111111111"), Question(id: 2, text: "222222222222222222222222"), Question(id: 3, text: "3333333333333333333"), Question(id: 4, text: "444444444444444444444444"), Question(id: 5, text: "55555555555555555555"), Question(id: 6, text: "6666666666666666666666666") ] @State private var currentQuestionIndex = 0 @State private var navigationDirection: NavigationDirection = .forward var body: some View { VStack(spacing: 20) { Text(questions[currentQuestionIndex].text) .id(questions[currentQuestionIndex].id) // Important for transition .transition(navigationDirection == .forward ? .slideRight : .slideLeft) .frame(maxWidth: .infinity, maxHeight: .infinity) HStack { Button("Previous") { moveToPreviousQuestion() } .disabled(currentQuestionIndex == 0) Spacer() Button("Next") { moveToNextQuestion() } .disabled(currentQuestionIndex == questions.count - 1) } } .padding() .animation(.easeInOut(duration: 1.0), value: currentQuestionIndex) } private func moveToNextQuestion() { if currentQuestionIndex < questions.count - 1 { navigationDirection = .forward currentQuestionIndex += 1 } } private func moveToPreviousQuestion() { if currentQuestionIndex > 0 { navigationDirection = .backward currentQuestionIndex -= 1 } } } enum NavigationDirection { case forward, backward }
0
0
480
Apr ’24
Animates wrongly at every switch of direction, animates right in same direction
If you run the app and keep pressing next, it works well. current question moves out towards left, next question appears from right to left. But if I switch the direction to previous, current question moves out towards left( should move out towards right) next question comes in from left to right( which is correct). Similar discrepancy happens when I switch direction from prev to next. Why is this so? import SwiftUI struct Question { let id: Int let text: String } extension AnyTransition { static var slideRight: AnyTransition { let insertion = AnyTransition.move(edge: .trailing) let removal = AnyTransition.move(edge: .leading) return .asymmetric(insertion: insertion, removal: removal) } static var slideLeft: AnyTransition { let insertion = AnyTransition.move(edge: .leading) let removal = AnyTransition.move(edge: .trailing) return .asymmetric(insertion: insertion, removal: removal) } } struct QuizView: View { let questions = [ Question(id: 1, text: "11111111111"), Question(id: 2, text: "222222222222222222222222"), Question(id: 3, text: "3333333333333333333"), Question(id: 4, text: "444444444444444444444444"), Question(id: 5, text: "55555555555555555555"), Question(id: 6, text: "6666666666666666666666666") ] @State private var currentQuestionIndex = 0 @State private var navigationDirection: NavigationDirection = .forward var body: some View { VStack(spacing: 20) { Text(questions[currentQuestionIndex].text) .id(questions[currentQuestionIndex].id) // Important for transition .transition(navigationDirection == .forward ? .slideRight : .slideLeft) .frame(maxWidth: .infinity, maxHeight: .infinity) HStack { Button("Previous") { moveToPreviousQuestion() } .disabled(currentQuestionIndex == 0) Spacer() Button("Next") { moveToNextQuestion() } .disabled(currentQuestionIndex == questions.count - 1) } } .padding() .animation(.easeInOut(duration: 1.0), value: currentQuestionIndex) } private func moveToNextQuestion() { if currentQuestionIndex < questions.count - 1 { navigationDirection = .forward currentQuestionIndex += 1 } } private func moveToPreviousQuestion() { if currentQuestionIndex > 0 { navigationDirection = .backward currentQuestionIndex -= 1 } } } enum NavigationDirection { case forward, backward }
1
0
445
Apr ’24
WithAnimation not working in certain orders of operations
Hi, I am currently working through a SwiftUI tutorial where I am adding words entered into a text field to be displayed in a list beneath it. While doing so, I noticed that when trying to animate the insertion of the word into the text field, it wasn't working. private func addNewWord(){ let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) guard answer.count > 0, !usedWords.contains(answer) else {return} newWord = "" withAnimation { usedWords.insert(answer, at: 0) } } But, when I switched around those last 2 statements, the following code works properly: private func addNewWord(){ let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) guard answer.count > 0, !usedWords.contains(answer) else {return} withAnimation { usedWords.insert(answer, at: 0) } newWord = "" } Does anyone know why this is ? Is there something that has to do with the withAnimation call not being the final part in the function call ? Does it have to do with altering different State variables before or after the withAnimation call ? Is this expected behavior or some type of bug? Please let me know, here is the entire SwiftUI Struct in case you want to run this and see for yourselves. Thank you (Currently using xcode 15.0 on OS Sonoma 14.3.1) struct ContentView: View { @State private var newWord : String = "" @State private var usedWords : [String] = [String]() @State private var rootWord : String = "" var body: some View { NavigationStack { List { Section { TextField("Enter your word", text: $newWord).textInputAutocapitalization(.never) } Section{ ForEach(usedWords, id: \.self){ word in HStack { Image(systemName: "\(word.count).circle") Text(word) } } } } .navigationTitle(rootWord) } .padding() .onSubmit() { addNewWord() } } private func addNewWord(){ let answer = newWord.lowercased().trimmingCharacters(in: .whitespacesAndNewlines) guard answer.count > 0, !usedWords.contains(answer) else {return} // newWord = "" withAnimation { usedWords.insert(answer, at: 0) } newWord = "" } }
0
0
539
Mar ’24
SectorMark foreground style colors
Dear all, I am using SwiftUI 15.2 and I have created a donut chart using SectorMark. Now, I have three values to show in the chart. When I set up the foregroundstyle, it returns orange, blu and green colors, whereas I'd like to have different colors (e.g. red, yellow and green). Chart(data, id: \.risultato) { dataItem in SectorMark(angle: .value("Type", dataItem.partite), innerRadius: .ratio(0.7), angularInset: 1.5) .foregroundStyle(by: .value("Type", dataItem.risultato)) .annotation(position: .overlay){ Text("\(dataItem.partite)") .font(.caption) } } .frame(height: 150) I'm reporting the final result here below. Do you know how I can customize them? Thanks in advance for your support, Andrea
2
1
1.5k
Feb ’24
Maximise Image above Form
Dear All, I want to maximise an Image above a Form. That should be easy enough but with the naive code, the Form is pushed out of the screen. I don't want to allow scrolling. This is my code: Image(systemName: "gear") .resizable() .scaledToFit() Form { Section("section 1") { Text("text 1") Text("text 2") Text("text 3") } Section("section 2") { Text("text 1") Text("text 2") Text("text 3") } } .scrollDisabled(true) Any hint on how to achieve that the Form is fully displayed and the Image dynamically maximised in the space that is left? Thanks in advance! Cheers F
2
0
833
Mar ’24
UIAlertAction has a poor color contrast ratio in destructive style
Hi Folks, I am reaching out for an iOS A11y color contrast issue, which I have also fired a bug report 12525894 but does not get any reply. The UIAlertAction in destructive style has a poor color contrast ratio of less than 4.5: 1 between text color and background color. This applies to any system/third party app which uses UIAlertViewController from UIKit and Alert from SwiftUI (Take the system Photos app as an example). Given that the "titleTextColor" property from UIAlertAction class is private, developers have no ways to modify the alert action to make it WCAG compatible. Here is an example of the Photo app alert. The text "Delete Photo" (#EB4E3D) should have a contrast ratio of at least 4.5 to 1 against its background(#EAEBE7) to comply to the Web Content Accessibility Guidelines (WCAG). Actual result: The text "Delete Photo" (#EB4E3D) has a contrast ratio of 3.08 to 1 against its background(#EAEBE7), which does not match 4.5:1 Web Content Accessibility Guidelines (WCAG).
0
0
555
Dec ’23