add app icon, rename to Sticker Cloner, pack thumbnails, sort list, smaller stickers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@@ -89,6 +89,15 @@ final class StickerStore {
|
||||
return urls
|
||||
}
|
||||
|
||||
func firstStickerURL(forPack name: String) -> URL? {
|
||||
let packDir = packDirectory(name: name)
|
||||
guard let files = try? fileManager.contentsOfDirectory(at: packDir, includingPropertiesForKeys: nil) else { return nil }
|
||||
return files
|
||||
.filter { $0.pathExtension == "png" }
|
||||
.sorted { $0.lastPathComponent < $1.lastPathComponent }
|
||||
.first
|
||||
}
|
||||
|
||||
// MARK: - Migration from old sandbox
|
||||
|
||||
private func migrateIfNeeded() {
|
||||
|
||||
@@ -1,70 +1,83 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "icon_58x58.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_87x87.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_120x90.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "2x",
|
||||
"size" : "60x45"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_180x135.png",
|
||||
"idiom" : "iphone",
|
||||
"scale" : "3x",
|
||||
"size" : "60x45"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_58x58.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "29x29"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_134x100.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "67x50"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_148x110.png",
|
||||
"idiom" : "ipad",
|
||||
"scale" : "2x",
|
||||
"size" : "74x55"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_1024x1024.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"scale" : "1x",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_54x40.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"scale" : "2x",
|
||||
"size" : "27x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_81x60.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"scale" : "3x",
|
||||
"size" : "27x20"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_64x48.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"scale" : "2x",
|
||||
"size" : "32x24"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_96x72.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"scale" : "3x",
|
||||
"size" : "32x24"
|
||||
},
|
||||
{
|
||||
"filename" : "icon_1024x768.png",
|
||||
"idiom" : "ios-marketing",
|
||||
"platform" : "ios",
|
||||
"scale" : "1x",
|
||||
|
||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
@@ -4,6 +4,15 @@ import UIKit
|
||||
final class StickerBrowserViewController: MSStickerBrowserViewController {
|
||||
private var stickers: [MSSticker] = []
|
||||
|
||||
init() {
|
||||
super.init(stickerSize: .small)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) is not supported")
|
||||
}
|
||||
|
||||
func reloadStickers() {
|
||||
let urls = StickerStore.shared.stickerFileURLs()
|
||||
stickers = urls.compactMap { url in
|
||||
|
||||
@@ -261,7 +261,7 @@
|
||||
DEVELOPMENT_TEAM = NG5W75WE8U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "StickerCloner MessagesExtension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = StickerCloner;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Sticker Cloner";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -291,7 +291,7 @@
|
||||
DEVELOPMENT_TEAM = NG5W75WE8U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "StickerCloner MessagesExtension/Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = StickerCloner;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Sticker Cloner";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
@@ -441,6 +441,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = NG5W75WE8U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Sticker Cloner";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
@@ -466,6 +467,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = NG5W75WE8U;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Sticker Cloner";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct AddPackView: View {
|
||||
@State private var urlText = ""
|
||||
@State private var isLoading = false
|
||||
@State private var errorMessage: String?
|
||||
@State private var packs: [SavedPack] = StickerStore.shared.savedPacks
|
||||
.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == .orderedAscending }
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -42,7 +44,15 @@ struct AddPackView: View {
|
||||
if !packs.isEmpty {
|
||||
Section("Saved Packs") {
|
||||
ForEach(packs, id: \.name) { pack in
|
||||
HStack {
|
||||
HStack(spacing: 12) {
|
||||
if let url = StickerStore.shared.firstStickerURL(forPack: pack.name),
|
||||
let data = try? Data(contentsOf: url),
|
||||
let uiImage = UIImage(data: data) {
|
||||
Image(uiImage: uiImage)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 44, height: 44)
|
||||
}
|
||||
VStack(alignment: .leading) {
|
||||
Text(pack.title)
|
||||
Text("\(pack.stickerCount) stickers")
|
||||
@@ -56,7 +66,7 @@ struct AddPackView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Sticker Packs")
|
||||
.navigationTitle("Sticker Cloner")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
@@ -78,6 +88,7 @@ struct AddPackView: View {
|
||||
let response = try await StickerClonerAPI.fetchStickerSet(name: name)
|
||||
try await StickerStore.shared.addPack(response: response)
|
||||
packs = StickerStore.shared.savedPacks
|
||||
.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == .orderedAscending }
|
||||
urlText = ""
|
||||
} catch {
|
||||
errorMessage = error.localizedDescription
|
||||
@@ -91,6 +102,7 @@ struct AddPackView: View {
|
||||
StickerStore.shared.removePack(name: packs[index].name)
|
||||
}
|
||||
packs = StickerStore.shared.savedPacks
|
||||
.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == .orderedAscending }
|
||||
}
|
||||
|
||||
private func extractPackName(from input: String) -> String {
|
||||
|
||||
|
After Width: | Height: | Size: 66 KiB |
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "AppIcon.png",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
|
||||