Skip to content

Commit

Permalink
Merge pull request #1141 from anyproto/ios-2381-improve-iconview
Browse files Browse the repository at this point in the history
iOS-2381 IconView | Migrate to swiftui api 🤞
  • Loading branch information
mgolovko authored Apr 8, 2024
2 parents bfb32b2 + a340589 commit b7143d6
Show file tree
Hide file tree
Showing 34 changed files with 371 additions and 811 deletions.
172 changes: 72 additions & 100 deletions Anytype.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension BundledRelationsValueProvider {
}

var objectIconImageWithPlaceholder: Icon {
return objectIconImage ?? .object(.placeholder(title.first))
return objectIconImage ?? .object(.placeholder(title))
}

var objectType: ObjectType {
Expand Down
2 changes: 1 addition & 1 deletion Anytype/Sources/Models/Relations/RelationsBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ private extension RelationsBuilder {
if relationDetails.key == BundledRelationKey.setOf.rawValue, objectDetail.isDeleted {
return Relation.Object.Option(
id: valueId,
icon: .object(.placeholder(nil)),
icon: .object(.placeholder("")),
title: Loc.deleted,
type: "",
isArchived: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct ObjectIconImageFontSet {
case .placeholder:
return placeholderImageFont
}
case .image, .asset, .cycle, .square, .squircle:
case .image, .asset:
return nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ struct ObjectIconImageGuidelineSet {
return staticImageGuideline
case .image:
return staticImageGuideline
case .square, .cycle, .squircle:
return nil
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation
import SwiftUI
import AnytypeCore

struct CircleGradientView: View {

private let storage = IconGradientStorage()
private let gradientInfo: IconGradient

init(gradientId: GradientId) {
self.gradientInfo = storage.gradient(for: gradientId.rawValue)
}

var body: some View {
GeometryReader { reader in
RadialGradient(
colors: [
gradientInfo.centerColor.suColor,
gradientInfo.roundColor.suColor
],
center: .center,
startRadius: 0,
endRadius: reader.size.width * 0.5
)
.clipShape(Circle())
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import Foundation
import SwiftUI

struct ImageCharIconView: View {

private struct Config {
let side: CGFloat
let fontSize: CGFloat

static let zero = Config(side: 0, fontSize: 0)
}

private static let configs = [
Config(side: 16, fontSize: 12),
Config(side: 18, fontSize: 14),
Config(side: 40, fontSize: 24),
Config(side: 48, fontSize: 30),
Config(side: 64, fontSize: 36),
Config(side: 80, fontSize: 48)
].sorted(by: { $0.side > $1.side }) // Order by DESC side for simple search

let text: String

var body: some View {
GeometryReader { reader in
ZStack(alignment: .center) {
Text(text.prefix(1).uppercased())
.foregroundColor(Color.Text.white)
.font(font(size: reader.size))
}
.frame(width: reader.size.width, height: reader.size.height)
}
}

private func font(size: CGSize) -> Font {
let side = min(size.width, size.height)
let config = Self.configs.first(where: { $0.side <= side }) ?? Self.configs.last ?? .zero
return AnytypeFontBuilder.font(name: .inter, size: config.fontSize, weight: .semibold)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation
import SwiftUI

struct ImageIdIconView: View {

let imageId: String

var body: some View {
GeometryReader { reader in
let side = min(reader.size.width, reader.size.height)
AsyncImage(
url: ImageMetadata(id: imageId, width: .width(side)).contentUrl
) { image in
image.resizable().scaledToFill()
} placeholder: {
EmptyView()
}
.frame(width: side, height: side)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,10 @@ import Services

enum Icon: Hashable, Equatable {
case object(ObjectIcon)

// Container
case cycle(Icon.Content)
case square(Icon.Content)
case squircle(Icon.Content)

// Without Container
case asset(ImageAsset)
case image(UIImage)
}

extension Icon {
enum Content: Hashable, Equatable {
case char(String)
case asset(ImageAsset)
case image(UIImage)
case imageId(String)
}
}

extension ObjectIcon {
var icon: Icon {
return .object(self)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,37 @@
import Foundation
import SwiftUI
import Services

struct IconView: View {

let icon: Icon?

@State private var task: Task<Void, Never>?
@State private var size: CGSize?
@State private var placeholderImage: UIImage?
@State private var image: UIImage?
@State private var id = UUID()

@Environment(\.isEnabled) private var isEnable
@Environment(\.redactionReasons) var redactionReasons

// MARK: - Public properties

var body: some View {
content
.readSize { newSize in
size = newSize
}
.onChange(of: isEnable) { isEnable in
updateIcon(isEnable: isEnable)
}
.onChange(of: size) { size in
updateIcon(size: size)
}
.onChange(of: icon) { icon in
// Icon field from struct contains old value
updateIcon(icon)
switch icon {
case .object(let objectIcon):
ObjectIconView(icon: objectIcon)
case .asset(let imageAsset):
Image(asset: imageAsset)
.buttonDynamicForegroundColor()
case .image(let uIImage):
Image(uiImage: uIImage)
.buttonDynamicForegroundColor()
case nil:
EmptyView()
}
.frame(idealWidth: 30, idealHeight: 30) // Default frame
.id(id)
}

// Root view should be a image view for apply initial frame.
// Don't use a viewbuilder, because view builder make a container
private var content: Image {
if redactionReasons.contains(.placeholder) {
return systemPlaceholder
} else if let image {
return Image(uiImage: image)
} else if let placeholderImage {
return Image(uiImage: placeholderImage)
} else {
return systemPlaceholder
}
}

extension IconView {
init(asset: ImageAsset) {
self = IconView(icon: .asset(asset))
}

private var systemPlaceholder: Image {
// Empty image for native placeholder
Image(uiImage: UIImage())
.resizable()
init(object: ObjectIcon) {
self = IconView(icon: .object(object))
}

private func updateIcon(_ newIcon: Icon? = nil, size newSize: CGSize? = nil, isEnable newIsEnable: Bool? = nil) {
task?.cancel()
guard let icon = newIcon ?? icon, let size = newSize ?? size else {
placeholderImage = nil
image = nil
return
}
let isEnable = newIsEnable ?? isEnable
let maker = IconMaker(icon: icon, size: size, iconContext: IconContext(isEnabled: isEnable))
if let imageFromCache = maker.makeFromCache() {
image = imageFromCache
} else {
task = Task { @MainActor in
placeholderImage = maker.makePlaceholder()
image = await maker.make()
}
}
init(uiImage: UIImage) {
self = IconView(icon: .image(uiImage))
}
}

This file was deleted.

This file was deleted.

Loading

0 comments on commit b7143d6

Please sign in to comment.