Почему модификатор кадра по-прежнему влияет на другие представления при присоединении к нулевому изображению?
self.imagesViewModel.allImages[post.imageContentURL]?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171, alignment: .center)
.clipShape(Rectangle())
.shadow(radius: 1)
Предполагается, что этот код ничего не делает, когда возвращаемое значение изображения nil
. Но по какой-то причине.frame
модификатор по-прежнему влияет на другие элементы внутри представления прокрутки. Это приводит к тому, что навигационные ссылки блокируются физически, но не визуально.
Я подтвердил, что когда .frame
удаляется, все работает так, как я задумал. Но мне нужно установить ограничения на изображения, поэтому я не могу обойтись без чего-то подобного.
Я попытался разместить код внутри оператора if, чтобы проверить nil
и отображать только при возвращении изображения, но это не сработало. Другие варианты этого кода также не работают.
if self.imagesViewModel.allImages[post.imageContentURL] != nil {
self.imagesViewModel.allImages[post.imageContentURL]?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171)
.clipShape(Rectangle())
.shadow(radius: 1)
}
Я также пытался установить минимальные значения на ноль, но безуспешно.
Мне интересно, связано ли это с тем, что я использую Xcode 12 beta 5, или с использованием нового жизненного цикла приложения SwiftUI. Когда я запускаю код на своем iPhone (iOS 14), я получаю те же физические препятствия, но добавляю визуальный интервал между элементами scrollview.
Вот упрощенная версия моего кода, которая воспроизводит мою проблему:
import SwiftUI
import Combine
struct Post: Codable, Identifiable {
var id: Int
var text: String
var imageContentURL: String?
}
class PostsViewModel: ObservableObject {
@Published var posts: [Post] = []
}
class ImagesViewModel: ObservableObject {
@Published var allImages: [String?: Image] = [:]
}
/// The View of the content displayed in Home
struct PostLayout: View {
@EnvironmentObject var imagesViewModel: ImagesViewModel
var post: Post
init(post: Post) {
self.post = post
}
var body: some View {
VStack {
// This is the navigation link that gets obstructed
NavigationLink(destination: SomeOtherView()) {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 48, height: 48)
.clipShape(Circle())
}
Text(self.post.text)
// MARK: This is the problem code, border added for clarity
self.imagesViewModel.allImages[self.post.imageContentURL]?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171)
.clipShape(Rectangle())
.shadow(radius: 1)
.border(Color.black, width: 1)
}
.border(Color.black, width: 1)
}
}
/// View that is navigated to from Home
struct SomeOtherView: View {
var body: some View {
Text("SomeOtherView")
}
}
/// Main displayed view
struct Home: View {
@EnvironmentObject var postsViewModel: PostsViewModel
@EnvironmentObject var imagesViewModel: ImagesViewModel
var body: some View {
NavigationView {
ScrollView {
ForEach(self.postsViewModel.posts) { post in
PostLayout(post: post)
}
}
.onAppear() {
// All displayed content created here
var post1 = Post(id: 0, text: "Content")
let post2 = Post(id: 1, text: "Content")
var post3 = Post(id: 2, text: "Content")
let post4 = Post(id: 3, text: "Content")
let imageURL = "someImageURL"
self.imagesViewModel.allImages[imageURL] = Image(systemName: "circle")
// Only two posts are supposed to have an imageURL
post1.imageContentURL = imageURL
post3.imageContentURL = imageURL
self.postsViewModel.posts.append(contentsOf: [post1, post2, post3, post4])
}
.navigationTitle("Home")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Home()
.environmentObject(PostsViewModel())
.environmentObject(ImagesViewModel())
}
}
4 ответа
Оказывается, проблема не в nil
или модификатор кадра.
Изображение фактически незримо выходило за границы .clipShape(Rectangle())
и блокировал NavigationLink. Решение - использовать комбинацию.contentShape()
а также .clipped
.
self.imagesViewModel.allImages[self.post.imageContentURL]?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171)
.contentShape(Rectangle())
.clipped()
.shadow(radius: 1)
Я обнаружил этот ответ из сообщения здесь: Обрезанное изображение вызывает TapAction за пределами кадра
Как вы уже заметили, .frame
модификатор не зависит от изображения. Он будет работать, даже если изображениеnil
.
Что вы можете сделать, так это проверить, Image
можно загрузить. Вы можете сделать это, используяUIImage(named:)
(который возвращает необязательный):
@ViewBuilder
var body: some View {
if UIImage(named: "imageName") != nil {
Image("imageName")
.resizable()
.scaledToFill()
.frame(width: 343, height: 171, alignment: .center)
...
}
}
или, альтернативно:
@ViewBuilder
var body: some View {
if imagesViewModel.allImages[post.imageContentURL] != nil {
imagesViewModel.allImages[post.imageContentURL]!
.resizable()
.scaledToFill()
.frame(width: 343, height: 171, alignment: .center)
...
}
}
РЕДАКТИРОВАТЬ
Нет необходимости в дополнительном String?
в дикт. Просто замените его на:
@Published var allImages: [String: Image] = [:]
Затем используйте if
оператор, чтобы проверить, не является ли изображение равным нулю (в Xcode 12 вы можете использовать if-let
также):
if self.post.imageContentURL != nil {
self.imagesViewModel.allImages[self.post.imageContentURL!]?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171)
.clipShape(Rectangle())
.shadow(radius: 1)
.border(Color.black, width: 1)
}
Вы можете просто использовать if-let (Xcode 12+):
if let image = self.imagesViewModel.allImages[post.imageContentURL]? {
image
.resizable()
.scaledToFill()
.frame(width: 343, height: 171, alignment: .center)
.clipShape(Rectangle())
.shadow(radius: 1)
}
Скорее всего, на самом изображении есть проблемы, но представление типа SwiftUI Image
существует. Image
больше похоже UIImageView
чем UIImage
, хотя на самом деле бесполезно думать об этом как об одном из них. Вероятно, вы хотите:
var body: some View {
if imageView == nil {
EmptyView()
} else {
imageView?
.resizable()
.scaledToFill()
.frame(width: 343, height: 171, alignment: .center)
.clipShape(Rectangle())
.shadow(radius: 1)
.border(Color.black)
}
}