Compare commits
No commits in common. "1a767ff9103ae5b7fae2e3a5e78969b41819b786" and "f848bbf7c424d06a113f2a122e75662f75d5bfc3" have entirely different histories.
1a767ff910
...
f848bbf7c4
|
@ -56,7 +56,6 @@ private let imageType = UTType.image.identifier
|
||||||
private let mp4Type = UTType.mpeg4Movie.identifier
|
private let mp4Type = UTType.mpeg4Movie.identifier
|
||||||
private let quickTimeType = UTType.quickTimeMovie.identifier
|
private let quickTimeType = UTType.quickTimeMovie.identifier
|
||||||
private let dataType = UTType.data.identifier
|
private let dataType = UTType.data.identifier
|
||||||
private let gifType = UTType.gif.identifier
|
|
||||||
|
|
||||||
extension CompositionAttachment: NSItemProviderWriting {
|
extension CompositionAttachment: NSItemProviderWriting {
|
||||||
static var writableTypeIdentifiersForItemProvider: [String] {
|
static var writableTypeIdentifiersForItemProvider: [String] {
|
||||||
|
@ -96,22 +95,20 @@ extension CompositionAttachment: NSItemProviderReading {
|
||||||
[typeIdentifier] + UIImage.readableTypeIdentifiersForItemProvider + [mp4Type, quickTimeType] + NSURL.readableTypeIdentifiersForItemProvider
|
[typeIdentifier] + UIImage.readableTypeIdentifiersForItemProvider + [mp4Type, quickTimeType] + NSURL.readableTypeIdentifiersForItemProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> CompositionAttachment {
|
static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> Self {
|
||||||
if typeIdentifier == CompositionAttachment.typeIdentifier {
|
if typeIdentifier == CompositionAttachment.typeIdentifier {
|
||||||
return try PropertyListDecoder().decode(CompositionAttachment.self, from: data)
|
return try PropertyListDecoder().decode(Self.self, from: data)
|
||||||
} else if typeIdentifier == gifType {
|
|
||||||
return CompositionAttachment(data: .gif(data))
|
|
||||||
} else if UIImage.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let image = try? UIImage.object(withItemProviderData: data, typeIdentifier: typeIdentifier) {
|
} else if UIImage.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let image = try? UIImage.object(withItemProviderData: data, typeIdentifier: typeIdentifier) {
|
||||||
return CompositionAttachment(data: .image(image))
|
return CompositionAttachment(data: .image(image)) as! Self
|
||||||
} else if let type = UTType(typeIdentifier), type == .mpeg4Movie || type == .quickTimeMovie {
|
} else if let type = UTType(typeIdentifier), type == .mpeg4Movie || type == .quickTimeMovie {
|
||||||
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
||||||
let temporaryFileName = ProcessInfo().globallyUniqueString
|
let temporaryFileName = ProcessInfo().globallyUniqueString
|
||||||
let fileExt = type.preferredFilenameExtension!
|
let fileExt = type.preferredFilenameExtension!
|
||||||
let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(temporaryFileName).appendingPathExtension(fileExt)
|
let temporaryFileURL = temporaryDirectoryURL.appendingPathComponent(temporaryFileName).appendingPathExtension(fileExt)
|
||||||
try data.write(to: temporaryFileURL)
|
try data.write(to: temporaryFileURL)
|
||||||
return CompositionAttachment(data: .video(temporaryFileURL))
|
return CompositionAttachment(data: .video(temporaryFileURL)) as! Self
|
||||||
} else if NSURL.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let url = try? NSURL.object(withItemProviderData: data, typeIdentifier: typeIdentifier) as URL {
|
} else if NSURL.readableTypeIdentifiersForItemProvider.contains(typeIdentifier), let url = try? NSURL.object(withItemProviderData: data, typeIdentifier: typeIdentifier) as URL {
|
||||||
return CompositionAttachment(data: .video(url))
|
return CompositionAttachment(data: .video(url)) as! Self
|
||||||
} else {
|
} else {
|
||||||
throw ItemProviderError.incompatibleTypeIdentifier
|
throw ItemProviderError.incompatibleTypeIdentifier
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ enum CompositionAttachmentData {
|
||||||
case image(UIImage)
|
case image(UIImage)
|
||||||
case video(URL)
|
case video(URL)
|
||||||
case drawing(PKDrawing)
|
case drawing(PKDrawing)
|
||||||
case gif(Data)
|
|
||||||
|
|
||||||
var type: AttachmentType {
|
var type: AttachmentType {
|
||||||
switch self {
|
switch self {
|
||||||
|
@ -28,8 +27,6 @@ enum CompositionAttachmentData {
|
||||||
return .video
|
return .video
|
||||||
case .drawing(_):
|
case .drawing(_):
|
||||||
return .image
|
return .image
|
||||||
case .gif(_):
|
|
||||||
return .image
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,8 +119,6 @@ enum CompositionAttachmentData {
|
||||||
case let .drawing(drawing):
|
case let .drawing(drawing):
|
||||||
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
let image = drawing.imageInLightMode(from: drawing.bounds, scale: 1)
|
||||||
completion(.success((image.pngData()!, .png)))
|
completion(.success((image.pngData()!, .png)))
|
||||||
case let .gif(data):
|
|
||||||
completion(.success((data, .gif)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +191,6 @@ extension CompositionAttachmentData: Codable {
|
||||||
try container.encode("drawing", forKey: .type)
|
try container.encode("drawing", forKey: .type)
|
||||||
let drawingData = drawing.dataRepresentation()
|
let drawingData = drawing.dataRepresentation()
|
||||||
try container.encode(drawingData, forKey: .drawing)
|
try container.encode(drawingData, forKey: .drawing)
|
||||||
case .gif(_):
|
|
||||||
throw EncodingError.invalidValue(self, EncodingError.Context(codingPath: [], debugDescription: "gif CompositionAttachments cannot be encoded"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +214,7 @@ extension CompositionAttachmentData: Codable {
|
||||||
let drawing = try PKDrawing(data: drawingData)
|
let drawing = try PKDrawing(data: drawingData)
|
||||||
self = .drawing(drawing)
|
self = .drawing(drawing)
|
||||||
default:
|
default:
|
||||||
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "CompositionAttachment type must be one of image, asset, or drawing")
|
throw DecodingError.dataCorruptedError(forKey: .type, in: container, debugDescription: "CompositionAttachment type must be one of 'image' or 'asset'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,18 @@ import AVKit
|
||||||
|
|
||||||
class AssetPreviewViewController: UIViewController {
|
class AssetPreviewViewController: UIViewController {
|
||||||
|
|
||||||
let asset: PHAsset
|
let attachment: CompositionAttachmentData
|
||||||
|
|
||||||
init(asset: PHAsset) {
|
init(attachment: CompositionAttachmentData) {
|
||||||
self.asset = asset
|
self.attachment = attachment
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convenience init(asset: PHAsset) {
|
||||||
|
self.init(attachment: .asset(asset))
|
||||||
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
@ -30,17 +34,27 @@ class AssetPreviewViewController: UIViewController {
|
||||||
|
|
||||||
view.backgroundColor = .black
|
view.backgroundColor = .black
|
||||||
|
|
||||||
switch asset.mediaType {
|
switch attachment {
|
||||||
case .image:
|
case let .image(image):
|
||||||
if asset.mediaSubtypes.contains(.photoLive) {
|
showImage(image)
|
||||||
showLivePhoto(asset)
|
case let .video(url):
|
||||||
} else {
|
showVideo(asset: AVURLAsset(url: url))
|
||||||
showAssetImage(asset)
|
case let .asset(asset):
|
||||||
|
switch asset.mediaType {
|
||||||
|
case .image:
|
||||||
|
if asset.mediaSubtypes.contains(.photoLive) {
|
||||||
|
showLivePhoto(asset)
|
||||||
|
} else {
|
||||||
|
showAssetImage(asset)
|
||||||
|
}
|
||||||
|
case .video:
|
||||||
|
showAssetVideo(asset)
|
||||||
|
default:
|
||||||
|
fatalError("asset mediaType must be image or video")
|
||||||
}
|
}
|
||||||
case .video:
|
case let .drawing(drawing):
|
||||||
showAssetVideo(asset)
|
let image = drawing.imageInLightMode(from: drawing.bounds)
|
||||||
default:
|
showImage(image)
|
||||||
fatalError("asset mediaType must be image or video")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ struct ComposeAttachmentImage: View {
|
||||||
let attachment: CompositionAttachment
|
let attachment: CompositionAttachment
|
||||||
let fullSize: Bool
|
let fullSize: Bool
|
||||||
|
|
||||||
@State private var gifData: Data? = nil
|
|
||||||
@State private var image: UIImage? = nil
|
@State private var image: UIImage? = nil
|
||||||
@State private var imageContentMode: ContentMode = .fill
|
@State private var imageContentMode: ContentMode = .fill
|
||||||
@State private var imageBackgroundColor: Color = .black
|
@State private var imageBackgroundColor: Color = .black
|
||||||
|
@ -21,9 +20,7 @@ struct ComposeAttachmentImage: View {
|
||||||
@Environment(\.colorScheme) private var colorScheme: ColorScheme
|
@Environment(\.colorScheme) private var colorScheme: ColorScheme
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if let gifData {
|
if let image = image {
|
||||||
GIFViewWrapper(gifData: gifData)
|
|
||||||
} else if let image {
|
|
||||||
Image(uiImage: image)
|
Image(uiImage: image)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: imageContentMode)
|
.aspectRatio(contentMode: imageContentMode)
|
||||||
|
@ -57,23 +54,9 @@ struct ComposeAttachmentImage: View {
|
||||||
// currently only used as thumbnail in ComposeAttachmentRow
|
// currently only used as thumbnail in ComposeAttachmentRow
|
||||||
size = CGSize(width: 80, height: 80)
|
size = CGSize(width: 80, height: 80)
|
||||||
}
|
}
|
||||||
let isGIF = PHAssetResource.assetResources(for: asset).contains(where: { $0.uniformTypeIdentifier == UTType.gif.identifier })
|
PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: nil) { (image, _) in
|
||||||
if isGIF {
|
DispatchQueue.main.async {
|
||||||
PHImageManager.default().requestImageDataAndOrientation(for: asset, options: nil) { data, typeIdentifier, orientation, info in
|
self.image = image
|
||||||
if typeIdentifier == UTType.gif.identifier {
|
|
||||||
self.gifData = data
|
|
||||||
} else if let data {
|
|
||||||
let image = UIImage(data: data)
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.image = image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: nil) { (image, _) in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.image = image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .video(url):
|
case let .video(url):
|
||||||
|
@ -86,35 +69,10 @@ struct ComposeAttachmentImage: View {
|
||||||
image = drawing.imageInLightMode(from: drawing.bounds)
|
image = drawing.imageInLightMode(from: drawing.bounds)
|
||||||
imageContentMode = .fit
|
imageContentMode = .fit
|
||||||
imageBackgroundColor = .white
|
imageBackgroundColor = .white
|
||||||
case let .gif(data):
|
|
||||||
self.gifData = data
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct GIFViewWrapper: UIViewRepresentable {
|
|
||||||
typealias UIViewType = GIFImageView
|
|
||||||
|
|
||||||
@State private var controller: GIFController
|
|
||||||
|
|
||||||
init(gifData: Data) {
|
|
||||||
self._controller = State(wrappedValue: GIFController(gifData: gifData))
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeUIView(context: Context) -> GIFImageView {
|
|
||||||
let view = GIFImageView()
|
|
||||||
controller.attach(to: view)
|
|
||||||
controller.startAnimating()
|
|
||||||
view.contentMode = .scaleAspectFit
|
|
||||||
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
|
||||||
view.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateUIView(_ uiView: GIFImageView, context: Context) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ComposeAttachmentImage_Previews: PreviewProvider {
|
struct ComposeAttachmentImage_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
ComposeAttachmentImage(attachment: CompositionAttachment(data: .image(UIImage())), fullSize: false)
|
ComposeAttachmentImage(attachment: CompositionAttachment(data: .image(UIImage())), fullSize: false)
|
||||||
|
|
|
@ -120,7 +120,6 @@ struct ComposeEmojiTextField: UIViewRepresentable {
|
||||||
}
|
}
|
||||||
|
|
||||||
func textFieldDidEndEditing(_ textField: UITextField) {
|
func textFieldDidEndEditing(_ textField: UITextField) {
|
||||||
uiState.currentInput = nil
|
|
||||||
updateAutocompleteState(textField: textField)
|
updateAutocompleteState(textField: textField)
|
||||||
didEndEditing?()
|
didEndEditing?()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,24 +26,22 @@ struct ComposeToolbar: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
HStack(spacing: 0) {
|
HStack(spacing: 8) {
|
||||||
Button("CW") {
|
Button("CW") {
|
||||||
draft.contentWarningEnabled.toggle()
|
draft.contentWarningEnabled.toggle()
|
||||||
}
|
}
|
||||||
.accessibilityLabel(draft.contentWarningEnabled ? "Remove content warning" : "Add content warning")
|
.accessibilityLabel(draft.contentWarningEnabled ? "Remove content warning" : "Add content warning")
|
||||||
.padding(5)
|
|
||||||
.hoverEffect()
|
|
||||||
|
|
||||||
MenuPicker(selection: $draft.visibility, options: Self.visibilityOptions, buttonStyle: .iconOnly)
|
MenuPicker(selection: $draft.visibility, options: Self.visibilityOptions, buttonStyle: .iconOnly)
|
||||||
// // the button has a bunch of extra space by default, but combined with what we add it's too much
|
// the button has a bunch of extra space by default, but combined with what we add it's too much
|
||||||
// .padding(.horizontal, -8)
|
.padding(.horizontal, -8)
|
||||||
|
|
||||||
if mastodonController.instanceFeatures.localOnlyPosts {
|
if mastodonController.instanceFeatures.localOnlyPosts {
|
||||||
MenuPicker(selection: $draft.localOnly, options: [
|
MenuPicker(selection: $draft.localOnly, options: [
|
||||||
.init(value: true, title: "Local-only", subtitle: "Only \(mastodonController.accountInfo!.instanceURL.host!)", image: UIImage(named: "link.broken")),
|
.init(value: true, title: "Local-only", subtitle: "Only \(mastodonController.accountInfo!.instanceURL.host!)", image: UIImage(named: "link.broken")),
|
||||||
.init(value: false, title: "Federated", image: UIImage(systemName: "link"))
|
.init(value: false, title: "Federated", image: UIImage(systemName: "link"))
|
||||||
], buttonStyle: .iconOnly)
|
], buttonStyle: .iconOnly)
|
||||||
// .padding(.horizontal, -8)
|
.padding(.horizontal, -8)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let currentInput = uiState.currentInput, currentInput.toolbarElements.contains(.emojiPicker) {
|
if let currentInput = uiState.currentInput, currentInput.toolbarElements.contains(.emojiPicker) {
|
||||||
|
@ -52,8 +50,6 @@ struct ComposeToolbar: View {
|
||||||
}
|
}
|
||||||
.labelStyle(.iconOnly)
|
.labelStyle(.iconOnly)
|
||||||
.font(.system(size: imageSize))
|
.font(.system(size: imageSize))
|
||||||
.padding(5)
|
|
||||||
.hoverEffect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let currentInput = uiState.currentInput,
|
if let currentInput = uiState.currentInput,
|
||||||
|
@ -72,8 +68,6 @@ struct ComposeToolbar: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.accessibilityLabel(format.accessibilityLabel)
|
.accessibilityLabel(format.accessibilityLabel)
|
||||||
.padding(5)
|
|
||||||
.hoverEffect()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +76,6 @@ struct ComposeToolbar: View {
|
||||||
Button(action: self.draftsButtonPressed) {
|
Button(action: self.draftsButtonPressed) {
|
||||||
Text("Drafts")
|
Text("Drafts")
|
||||||
}
|
}
|
||||||
.padding(5)
|
|
||||||
.hoverEffect()
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 16)
|
.padding(.horizontal, 16)
|
||||||
.frame(minWidth: minWidth)
|
.frame(minWidth: minWidth)
|
||||||
|
|
|
@ -31,13 +31,6 @@ struct DraftsView: View {
|
||||||
} label: {
|
} label: {
|
||||||
DraftView(draft: draft)
|
DraftView(draft: draft)
|
||||||
}
|
}
|
||||||
.contextMenu {
|
|
||||||
Button(role: .destructive) {
|
|
||||||
draftsManager.remove(draft)
|
|
||||||
} label: {
|
|
||||||
Label("Delete Draft", systemImage: "trash")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onDrag {
|
.onDrag {
|
||||||
let activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: mastodonController.accountInfo!.id)
|
let activity = UserActivityManager.editDraftActivity(id: draft.id, accountID: mastodonController.accountInfo!.id)
|
||||||
activity.displaysAuxiliaryScene = true
|
activity.displaysAuxiliaryScene = true
|
||||||
|
|
|
@ -106,6 +106,13 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
|
snapshot.appendSections([.header, .pinned, .statuses])
|
||||||
|
snapshot.appendItems([.header(accountID)], toSection: .header)
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
|
||||||
|
state = .setupInitialSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||||
|
@ -174,18 +181,11 @@ class ProfileStatusesViewController: UIViewController, TimelineLikeCollectionVie
|
||||||
private func load() async {
|
private func load() async {
|
||||||
guard isViewLoaded,
|
guard isViewLoaded,
|
||||||
let accountID,
|
let accountID,
|
||||||
state == .unloaded,
|
state == .setupInitialSnapshot,
|
||||||
mastodonController.persistentContainer.account(for: accountID) != nil else {
|
mastodonController.persistentContainer.account(for: accountID) != nil else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
|
||||||
snapshot.appendSections([.header, .pinned, .statuses])
|
|
||||||
snapshot.appendItems([.header(accountID)], toSection: .header)
|
|
||||||
await apply(snapshot, animatingDifferences: false)
|
|
||||||
|
|
||||||
state = .setupInitialSnapshot
|
|
||||||
|
|
||||||
await controller.loadInitial()
|
await controller.loadInitial()
|
||||||
await tryLoadPinned()
|
await tryLoadPinned()
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,6 @@ class ProfileViewController: UIViewController {
|
||||||
assert(!animated)
|
assert(!animated)
|
||||||
// if old doesn't exist, we're selecting the initial view controller, so moving the header around isn't necessary
|
// if old doesn't exist, we're selecting the initial view controller, so moving the header around isn't necessary
|
||||||
new.initialHeaderMode = .createView
|
new.initialHeaderMode = .createView
|
||||||
new.view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
embedChild(new)
|
embedChild(new)
|
||||||
self.currentIndex = index
|
self.currentIndex = index
|
||||||
state = .idle
|
state = .idle
|
||||||
|
@ -222,7 +221,6 @@ class ProfileViewController: UIViewController {
|
||||||
new.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: additionalHeightNeededToMatchContentOffset, right: 0)
|
new.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: additionalHeightNeededToMatchContentOffset, right: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
new.view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
embedChild(new)
|
embedChild(new)
|
||||||
new.view.transform = CGAffineTransform(translationX: direction * view.bounds.width, y: yTranslationToMatchOldContentOffset)
|
new.view.transform = CGAffineTransform(translationX: direction * view.bounds.width, y: yTranslationToMatchOldContentOffset)
|
||||||
|
|
||||||
|
@ -258,7 +256,6 @@ class ProfileViewController: UIViewController {
|
||||||
animator.startAnimation()
|
animator.startAnimation()
|
||||||
} else {
|
} else {
|
||||||
old.removeViewAndController()
|
old.removeViewAndController()
|
||||||
new.view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
embedChild(new)
|
embedChild(new)
|
||||||
completion?(true)
|
completion?(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ struct MenuPicker<Value: Hashable>: UIViewRepresentable {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
button.accessibilityLabel = selectedOption.accessibilityLabel ?? selectedOption.title
|
button.accessibilityLabel = selectedOption.accessibilityLabel ?? selectedOption.title
|
||||||
button.isPointerInteractionEnabled = buttonStyle == .iconOnly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Option {
|
struct Option {
|
||||||
|
|
Loading…
Reference in New Issue