Open3

SwiftUI

NaNa

Grid のUIのコード

import SwiftUI

struct PremiumPlanView: View {

    @Binding var plans: [Plan]

    private let columns: [GridItem] = Array(repeating: .init(spacing: 16), count: 2)

    @State var selectedPlan: Plan.ProductId?

    var body: some View {
        Grid(horizontalSpacing: 16, verticalSpacing: 16) {

            if plans.count > 3 {
                GridRow {
                    ForEach(0..<2) { index in
                        RoundedRectangle(cornerRadius: 16)
                            .stroke(lineWidth: 2)
                            .foregroundColor(
                                Color.accentColor
                            )
                            .frame(height: 180)
                            .overlay {
                                VStack(alignment: .leading) {

                                    HStack(alignment: .top) {
                                        Text(plans[index].displayName)
                                            .font(.title3)
                                            .fontWeight(.black)
                                        Spacer()
                                        Image(
                                            systemName: isSelected(plan: plans[index]) ? "checkmark.circle.fill" : "circle"
                                        )
                                        .resizable()
                                        .scaledToFit()
                                        .frame(height: 17)
                                    }
                                    .frame(alignment: .top)
                                    .padding(.bottom, 8)

                                    ForEach(plans[index].displayDescriptions, id: \.self) { description in
                                        Text(description)
                                            .font(
                                                .system(
                                                    size: 14,
                                                    weight: .regular,
                                                    design: .default
                                                )
                                            )
                                    }
                                    Spacer()
                                    HStack {
                                        Spacer()
                                        Text(plans[index].displayPeriod)
                                            .font(
                                                .system(
                                                    size: 14,
                                                    weight: .medium,
                                                    design: .default
                                                )
                                            )
                                        Text(plans[index].displayPrice)
                                            .font(.title3)
                                            .fontWeight(.black)
                                    }
                                }
                                .foregroundColor(
                                    isSelected(plan: plans[index]) ?                                Color(.systemBackground) : Color.accentColor
                                )
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .padding(16)
                            }
                            .background(
                                isSelected(plan: plans[index]) ?                               Color.accentColor : Color(.systemBackground)
                            )
                            .mask {
                                RoundedRectangle(cornerRadius: 16)
                            }
                            .onTapGesture {
                                selectedPlan = plans[index].productId
                            }
                    }
                }
                GridRow {
                    ForEach(2..<4) { index in
                        RoundedRectangle(cornerRadius: 16)
                            .stroke(lineWidth: 2)
                            .foregroundColor(
                                Color.accentColor
                            )
                            .frame(height: 180)
                            .overlay {
                                VStack(alignment: .leading) {

                                    HStack(alignment: .top) {
                                        Text(plans[index].displayName)
                                            .font(.title3)
                                            .fontWeight(.black)
                                        Spacer()
                                        Image(
                                            systemName: isSelected(plan: plans[index]) ? "checkmark.circle.fill" : "circle"
                                        )
                                        .resizable()
                                        .scaledToFit()
                                        .frame(height: 17)
                                    }
                                    .frame(alignment: .top)
                                    .padding(.bottom, 8)

                                    ForEach(plans[index].displayDescriptions, id: \.self) { description in
                                        Text(description)
                                            .font(
                                                .system(
                                                    size: 14,
                                                    weight: .regular,
                                                    design: .default
                                                )
                                            )
                                    }
                                    Spacer()
                                    HStack {
                                        Spacer()
                                        Text(plans[index].displayPeriod)
                                            .font(
                                                .system(
                                                    size: 14,
                                                    weight: .medium,
                                                    design: .default
                                                )
                                            )
                                        Text(plans[index].displayPrice)
                                            .font(.title3)
                                            .fontWeight(.black)
                                    }
                                }
                                .foregroundColor(
                                    isSelected(plan: plans[index]) ?                                Color(.systemBackground) : Color.accentColor
                                )
                                .frame(maxWidth: .infinity, alignment: .leading)
                                .padding(16)
                            }
                            .background(
                                isSelected(plan: plans[index]) ?                               Color.accentColor : Color(.systemBackground)
                            )
                            .mask {
                                RoundedRectangle(cornerRadius: 16)
                            }
                            .onTapGesture {
                                selectedPlan = plans[index].productId
                            }
                    }
                }
            }

        }
        .padding(.horizontal, 16)
    }

    func isSelected(plan: Plan) -> Bool {
        return selectedPlan == plan.productId
    }
}

#Preview {
    PremiumPlanView(plans:
            .constant(
                [
//                    Plan(
//                        id: Plan.ProductId.monthlyFreeAdPlan.rawValue,
//                        displayName: "広告ゼロ\nプラン",
//                        displayPrice: "¥300",
//                        displayIntroductoryOfferPeriod: 1,
//                        displayIntroductoryOfferUnit: ""
//                    ),
//                    Plan(
//                        id: Plan.ProductId.monthlyReadaloudPlan.rawValue,
//                        displayName: "読み上げ\n強化プラン",
//                        displayPrice: "¥500",                         displayIntroductoryOfferPeriod: 1,
//                        displayIntroductoryOfferUnit: ""
//                    ),
//                    Plan(
//                        id: Plan.ProductId.monthlyRevisePlan.rawValue,
//                        displayName: "添削\n強化プラン",
//                        displayPrice: "¥500",                         displayIntroductoryOfferPeriod: 1,
//                        displayIntroductoryOfferUnit: ""
//                    ),
                    Plan(
                        id: Plan.ProductId.monthlyPremiumPlan.rawValue,
                        displayName: "よくばり\nプラン",
                        displayPrice: "¥800",                         displayIntroductoryOfferPeriod: 1,
                        displayIntroductoryOfferUnit: ""
                    )
                ]
            )
    )
}

NaNa

ボタンタップして翻訳

@State private var isTranslationPresenting = false

Button {
    isTranslationPresenting.toggle()
} label: {
    Text("日記を翻訳する")
    Spacer()
}
.translationPresentation(
    isPresented: $isTranslationPresenting,
    text: entry.displayContent
)