Selaa lähdekoodia

Polish paywall layout spacing and section structure.

Anchor the close button independently, rebalance vertical spacing, remove extra hero/benefits gaps, and add a trust row so the paywall uses space more intentionally and looks more professional.

Made-with: Cursor
huzaifahayat12 6 tuntia sitten
vanhempi
commit
48182e7586
1 muutettua tiedostoa jossa 38 lisäystä ja 31 poistoa
  1. 38 31
      classroom_app/ViewController.swift

+ 38 - 31
classroom_app/ViewController.swift

@@ -2637,7 +2637,7 @@ private extension ViewController {
2637 2637
         let contentStack = NSStackView()
2638 2638
         contentStack.translatesAutoresizingMaskIntoConstraints = false
2639 2639
         contentStack.orientation = .vertical
2640
-        contentStack.spacing = 10
2640
+        contentStack.spacing = 12
2641 2641
         contentStack.distribution = .fill
2642 2642
         contentStack.alignment = .centerX
2643 2643
         panel.addSubview(contentStack)
@@ -2650,9 +2650,6 @@ private extension ViewController {
2650 2650
         topRow.distribution = .fill
2651 2651
         topRow.spacing = 10
2652 2652
         topRow.addArrangedSubview(textLabel("Classroom Pro", font: NSFont.systemFont(ofSize: 27, weight: .bold), color: palette.textPrimary))
2653
-        let topSpacer = NSView()
2654
-        topSpacer.translatesAutoresizingMaskIntoConstraints = false
2655
-        topRow.addArrangedSubview(topSpacer)
2656 2653
         let closeButton = HoverButton(title: "✕", target: self, action: #selector(closePaywallClicked(_:)))
2657 2654
         closeButton.translatesAutoresizingMaskIntoConstraints = false
2658 2655
         closeButton.isBordered = false
@@ -2674,15 +2671,10 @@ private extension ViewController {
2674 2671
             closeButton.layer?.backgroundColor = (hovering ? hover : base).cgColor
2675 2672
             closeButton.contentTintColor = hovering ? (self.darkModeEnabled ? .white : self.palette.textPrimary) : self.palette.textSecondary
2676 2673
         }
2677
-        topRow.addArrangedSubview(closeButton)
2674
+        panel.addSubview(closeButton)
2678 2675
         contentStack.addArrangedSubview(topRow)
2679 2676
         topRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
2680 2677
 
2681
-        let heroTopSpacer = NSView()
2682
-        heroTopSpacer.translatesAutoresizingMaskIntoConstraints = false
2683
-        heroTopSpacer.heightAnchor.constraint(equalToConstant: 18).isActive = true
2684
-        contentStack.addArrangedSubview(heroTopSpacer)
2685
-
2686 2678
         let hero = roundedContainer(cornerRadius: 16, color: palette.sectionCard)
2687 2679
         hero.translatesAutoresizingMaskIntoConstraints = false
2688 2680
         styleSurface(hero, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
@@ -2710,11 +2702,6 @@ private extension ViewController {
2710 2702
             heroSubtitle.bottomAnchor.constraint(equalTo: hero.bottomAnchor, constant: -16)
2711 2703
         ])
2712 2704
 
2713
-        let benefitsTopSpacer = NSView()
2714
-        benefitsTopSpacer.translatesAutoresizingMaskIntoConstraints = false
2715
-        benefitsTopSpacer.heightAnchor.constraint(equalToConstant: 40).isActive = true
2716
-        contentStack.addArrangedSubview(benefitsTopSpacer)
2717
-
2718 2705
         let benefitsRow = NSStackView()
2719 2706
         benefitsRow.translatesAutoresizingMaskIntoConstraints = false
2720 2707
         benefitsRow.orientation = .horizontal
@@ -2728,11 +2715,6 @@ private extension ViewController {
2728 2715
         contentStack.addArrangedSubview(benefitsRow)
2729 2716
         benefitsRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
2730 2717
 
2731
-        let midTopSpacer = NSView()
2732
-        midTopSpacer.translatesAutoresizingMaskIntoConstraints = false
2733
-        midTopSpacer.heightAnchor.constraint(equalToConstant: 6).isActive = true
2734
-        contentStack.addArrangedSubview(midTopSpacer)
2735
-
2736 2718
         let plansRow = NSStackView()
2737 2719
         plansRow.translatesAutoresizingMaskIntoConstraints = false
2738 2720
         plansRow.orientation = .horizontal
@@ -2784,10 +2766,17 @@ private extension ViewController {
2784 2766
         plansRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
2785 2767
         updatePaywallPlanSelection()
2786 2768
 
2787
-        let midBottomSpacer = NSView()
2788
-        midBottomSpacer.translatesAutoresizingMaskIntoConstraints = false
2789
-        midBottomSpacer.heightAnchor.constraint(equalToConstant: 8).isActive = true
2790
-        contentStack.addArrangedSubview(midBottomSpacer)
2769
+        let trustRow = NSStackView()
2770
+        trustRow.translatesAutoresizingMaskIntoConstraints = false
2771
+        trustRow.orientation = .horizontal
2772
+        trustRow.spacing = 8
2773
+        trustRow.distribution = .fillEqually
2774
+        trustRow.alignment = .centerY
2775
+        trustRow.addArrangedSubview(paywallMetaItem(title: "Cancel anytime", subtitle: "No lock-in"))
2776
+        trustRow.addArrangedSubview(paywallMetaItem(title: "Instant access", subtitle: "Unlock all tools"))
2777
+        trustRow.addArrangedSubview(paywallMetaItem(title: "Secure billing", subtitle: "Handled by Apple"))
2778
+        contentStack.addArrangedSubview(trustRow)
2779
+        trustRow.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
2791 2780
 
2792 2781
         let offer = textLabel(paywallOfferText(for: selectedPremiumPlan), font: NSFont.systemFont(ofSize: 13, weight: .semibold), color: palette.textPrimary)
2793 2782
         offer.alignment = .center
@@ -2843,20 +2832,17 @@ private extension ViewController {
2843 2832
             secure.bottomAnchor.constraint(equalTo: secureWrap.bottomAnchor, constant: -4)
2844 2833
         ])
2845 2834
 
2846
-        let footerTopSpacer = NSView()
2847
-        footerTopSpacer.translatesAutoresizingMaskIntoConstraints = false
2848
-        contentStack.addArrangedSubview(footerTopSpacer)
2849
-        footerTopSpacer.heightAnchor.constraint(equalToConstant: 4).isActive = true
2850
-
2851 2835
         let footer = paywallFooterLinks()
2852 2836
         contentStack.addArrangedSubview(footer)
2853 2837
         footer.widthAnchor.constraint(equalTo: contentStack.widthAnchor).isActive = true
2854 2838
 
2855 2839
         NSLayoutConstraint.activate([
2840
+            closeButton.topAnchor.constraint(equalTo: panel.topAnchor, constant: 18),
2841
+            closeButton.trailingAnchor.constraint(equalTo: panel.trailingAnchor, constant: -18),
2856 2842
             contentStack.centerXAnchor.constraint(equalTo: panel.centerXAnchor),
2857 2843
             contentStack.widthAnchor.constraint(equalToConstant: paywallLayoutWidth),
2858
-            contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 52),
2859
-            contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -10)
2844
+            contentStack.topAnchor.constraint(equalTo: panel.topAnchor, constant: 80),
2845
+            contentStack.bottomAnchor.constraint(equalTo: panel.bottomAnchor, constant: -12)
2860 2846
         ])
2861 2847
 
2862 2848
         refreshPaywallStoreUI()
@@ -3044,6 +3030,27 @@ private extension ViewController {
3044 3030
         return stack
3045 3031
     }
3046 3032
 
3033
+    func paywallMetaItem(title: String, subtitle: String) -> NSView {
3034
+        let card = roundedContainer(cornerRadius: 12, color: palette.inputBackground)
3035
+        card.translatesAutoresizingMaskIntoConstraints = false
3036
+        card.heightAnchor.constraint(equalToConstant: 48).isActive = true
3037
+        styleSurface(card, borderColor: palette.inputBorder, borderWidth: 1, shadow: false)
3038
+
3039
+        let titleLabel = textLabel(title, font: NSFont.systemFont(ofSize: 11, weight: .semibold), color: palette.textPrimary)
3040
+        let subtitleLabel = textLabel(subtitle, font: NSFont.systemFont(ofSize: 10, weight: .medium), color: palette.textSecondary)
3041
+        card.addSubview(titleLabel)
3042
+        card.addSubview(subtitleLabel)
3043
+
3044
+        NSLayoutConstraint.activate([
3045
+            titleLabel.centerXAnchor.constraint(equalTo: card.centerXAnchor),
3046
+            titleLabel.topAnchor.constraint(equalTo: card.topAnchor, constant: 8),
3047
+            subtitleLabel.centerXAnchor.constraint(equalTo: card.centerXAnchor),
3048
+            subtitleLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 3)
3049
+        ])
3050
+
3051
+        return card
3052
+    }
3053
+
3047 3054
     func paywallBenefitItem(icon: String, text: String) -> NSView {
3048 3055
         let card = HoverTrackingView()
3049 3056
         card.translatesAutoresizingMaskIntoConstraints = false