Open1

SwiftUI: Same-type requirement makes generic parameter '...' non-generic;の件

kabeyakabeya

と出て、コンパイルできないことがあります。
今回詰まったのは.resizableをカスタムモディファイアで使用しようとしたときでした。

struct DynamicTypeIconSizeModifier<IconT> : ViewModifier where IconT: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize
    
    func body(content: Content) -> some View {
        let size = self.imageSize
        content
            .resizable() // ←エラーになる
            .frame(width: size, height: size)
    }
    
    var imageSize: CGFloat {
       //略
    }
}

そもそも.resizableViewではなくてImageにしか適用できないと分かりました[1]

それでwhere IconT : Imageと書くと冒頭のメッセージが出ます。
メッセージとしてはImageは具体的な型だから、これじゃノンジェネリックになっちゃうよね?というような感じだと思います。

Viewはプロトコルなので具体的な型は分からずジェネリックで良いのですが、Imagestructなのでwhere IconT : Imageと書いたらもうIconTは必ずImageとなってしまいます。

このため、extension Viewではなく、extension Imageで書くべき、ということになります。

struct DynamicTypeIconSizeModifier<IconT> : ViewModifier where IconT: View {
    @Environment(\.dynamicTypeSize) var dynamicTypeSize
    
    func body(content: Content) -> some View {
        let size = self.imageSize
        content
            //.resizable() // ←やめる
            .frame(width: size, height: size)
    }
    
    var imageSize: CGFloat {
       //略
    }
}

extension Image {
    func dynamicTypeIconSize() -> some View {
        self
            .resizable()
            .modifier(DynamicTypeIconSizeModifier<Image>())
    }
}

ちなみに上記の例では、.resizable().frame()も、両方extension Image内の1つの関数でやってしまえば良さそうなものですが、extensionではメンバ変数が持てないため、上記のように分けるしかないです。

脚注
  1. よくよく調べると.resizable自身もモディファイアではなくてImageのメソッドでした。 ↩︎