Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pathを活用してSwift Chartsの限界を超えろ / create-graph-usi...

Pathを活用してSwift Chartsの限界を超えろ / create-graph-using-path

Fuya Yamada

July 14, 2023
Tweet

More Decks by Fuya Yamada

Other Decks in Technology

Transcript

  1. © ZOZO, Inc. גࣜձࣾZOZO ϒϥϯυιϦϡʔγϣϯ։ൃຊ෦ / ϑϩϯτΤϯυ෦ WEAR iOSϒϩοΫ ࢁా

    ෨໵ (Fuya Yamada) ελόʹߦͬͯɺۤΈʹ଱͑ͳ͕Β ҿΉίʔώʔ͕޷͖ ʢຊ౰͸ίίΞ͕ྑ͍ʣ
  2. © ZOZO, Inc. ΞδΣϯμ • Swift Chartsͷ঺հ • Swift Chartsͷݶք

    • PathΛ༻͍ͯݶքಥഁ͢Δํ๏ • Swift Chartsʹ͸ͳ͍άϥϑͷ࣮૷ྫ • ·ͱΊ 4
  3. © ZOZO, Inc. Swift Chartsͷ঺հ Swift Chartsͱ͸ʁ ◦ iOS 16͔Βར༻Ͱ͖Δ

    SwiftUI ͷάϥϑඳըϥΠϒϥϦ ◦ ๮ɺંΕઢɺࢄ෍ਤΛ͸͡Ίͱ͢Δ6छྨͷඳը͕Մೳ 5
  4. © ZOZO, Inc. PathͷͰ͖Δ͜ͱ Path { path in path.move(to: CGPoint(x:

    0, y: 0)) path.addLine(to: CGPoint(x: 100, y: 0)) } 9 path.closeSubpath() // ύεΛด͡Δ path.addArc( center: CGPoint(x: 100, y: 100), radius: 100, startAngle: .degrees(-90), endAngle: .degrees(0), clockwise: false ) ࢝఺↓
  5. © ZOZO, Inc. ShapeϓϩτίϧΛར༻ ඳըՄೳͳਤܗΛఆٛ͢ΔͨΊͷΠϯλʔϑΣʔε PathͰԁάϥϑΛ࣮૷ 10 public protocol Shape

    : Animatable, View { func path(in rect: CGRect) -> Path } Animatable ೚ҙͷ஋͕มԽͨ࣌͠ɺ ΞχϝʔγϣϯΛద༻ path(in:)ϝιου ༩͑ΒΕͨCGRect (ඳըྖҬ)಺ ʹඳը͢ΔPath
  6. © ZOZO, Inc. struct ArcView: Shape { var startAngle: Double

    var endAngle: Double var animatableData: Double { get { endAngle } set { endAngle = newValue } } func path(in rect: CGRect) -> Path { let rotationAdjustment = Angle(degrees: 90) let adjustedStartAngle = Angle(degrees: startAngle) - rotationAdjustment let adjustedEndAngle = Angle(degrees: endAngle) - rotationAdjustment return Path { p in p.addArc( center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: adjustedStartAngle, endAngle: adjustedEndAngle, clockwise: false ) p.addLine(to: CGPoint(x: rect.midX, y: rect.midY)) p.closeSubpath() } } } ArcViewͷ࣮૷ - શମ૾ 12 ArcView
  7. © ZOZO, Inc. struct ArcView: Shape { var startAngle: Double

    var endAngle: Double var animatableData: Double { get { endAngle } set { endAngle = newValue } } func path(in rect: CGRect) -> Path { let rotationAdjustment = Angle(degrees: 90) let adjustedStartAngle = Angle(degrees: startAngle) - rotationAdjustment let adjustedEndAngle = Angle(degrees: endAngle) - rotationAdjustment return Path { p in p.addArc( center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: adjustedStartAngle, endAngle: adjustedEndAngle, clockwise: false ) p.addLine(to: CGPoint(x: rect.midX, y: rect.midY)) p.closeSubpath() } } } ArcViewͷ࣮૷ AnimatableData 
 ϓϩύςΟ e.g. endAngleʢऴྃ֯౓ʣΛ ࢦఆ͢ΔͱArcViewͷׂ߹͕ ૿Ճ͢ΔΞχϝʔγϣϯ var animatableData: Double { get { endAngle } set { endAngle = newValue } } 13
  8. © ZOZO, Inc. struct ArcView: Shape { var startAngle: Double

    var endAngle: Double var animatableData: Double { get { endAngle } set { endAngle = newValue } } func path(in rect: CGRect) -> Path { let rotationAdjustment = Angle(degrees: 90) let adjustedStartAngle = Angle(degrees: startAngle) - rotationAdjustment let adjustedEndAngle = Angle(degrees: endAngle) - rotationAdjustment return Path { p in p.addArc( center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: adjustedStartAngle, endAngle: adjustedEndAngle, clockwise: false ) p.addLine(to: CGPoint(x: rect.midX, y: rect.midY)) p.closeSubpath() } } } ArcViewͷ࣮૷ 0౓Λ্෦ʹ͢ΔͨΊʹ ֯౓ௐ੔ let rotationAdjustment = Angle(degrees: 90) let adjustedStartAngle = Angle(degrees: startAngle) - rotationAdjustment let adjustedEndAngle = Angle(degrees: endAngle) - rotationAdjustment 14 0° 0°
  9. © ZOZO, Inc. struct ArcView: Shape { var startAngle: Double

    var endAngle: Double var animatableData: Double { get { endAngle } set { endAngle = newValue } } func path(in rect: CGRect) -> Path { let rotationAdjustment = Angle(degrees: 90) let adjustedStartAngle = Angle(degrees: startAngle) - rotationAdjustment let adjustedEndAngle = Angle(degrees: endAngle) - rotationAdjustment return Path { p in p.addArc ( center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: adjustedStartAngle, endAngle: adjustedEndAngle, clockwise: false ) p.addLine(to: CGPoint(x: rect.midX, y: rect.midY)) p.closeSubpath() } } } ArcViewͷ࣮૷ ԁހͷඳը addArc + addLine & close return Path { p in p.addArc ( center: CGPoint(x: rect.midX, y: rect.midY), radius: rect.width / 2, startAngle: adjustedStartAngle, endAngle: adjustedEndAngle, clockwise: false ) p.addLine(to: CGPoint(x: rect.midX, y: rect.midY)) p.closeSubpath() } 15
  10. © ZOZO, Inc. PathͰԁάϥϑΛ࣮૷ 16 ԁάϥϑ׬੒ struct PieChartView: View {

    var body: some View { ZStack { ArcView(startAngle: 0, endAngle: 360) .fill(Color("green")) ArcView(startAngle: 0, endAngle: 250) .fill(Color("blue")) ArcView(startAngle: 0, endAngle: 140) .fill(Color("red")) } } }
  11. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 20
  12. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 21
  13. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 22 ลͷ਺Λఆٛ let sides: Int // ลͷ਺
  14. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 23
  15. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 24 Ҿ਺ CGRect ͔Βத৺఺ɺ൒ܘɺ֯౓ͷࢉग़ let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2)
  16. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 25
  17. © ZOZO, Inc. struct PolygonShape: Shape { let sides: Int

    // ลͷ਺ func path(in rect: CGRect) -> Path { let centerX = rect.width / 2 let centerY = rect.height / 2 let radius = min(rect.width, rect.height) / 2 let angle = .pi * 2 / Double(sides) let offsetAngle = -(Double.pi / 2) // 0౓Λ্෦ʹ͢ΔͨΊʹ֯౓ௐ੔ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() } } } Ϩʔμʔνϟʔτ - શମ૾ 26 ֤௖఺ͷ֯౓Λܭࢉ ࠷ॳͷ௖఺ͳΒ͹ύεͷ։࢝఺Λઃఆ ͦΕҎ֎ͳΒ௚ઢΛҾ͘ return Path { path in for index in 0..<sides { let currentAngle = angle * Double(index) + offsetAngle let x = centerX + cos(currentAngle) * radius let y = centerY + sin(currentAngle) * radius if index == 0 { path.move(to: CGPoint(x: x, y: y)) } else { path.addLine(to: CGPoint(x: x, y: y)) } } path.closeSubpath() }