diff --git a/AltSwiftUI.xcodeproj/project.pbxproj b/AltSwiftUI.xcodeproj/project.pbxproj index 8edc5d5..7a99f17 100644 --- a/AltSwiftUI.xcodeproj/project.pbxproj +++ b/AltSwiftUI.xcodeproj/project.pbxproj @@ -84,6 +84,7 @@ 7D2D4D7025148269000F5DDC /* RoundedRectangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D2D4D6A25148269000F5DDC /* RoundedRectangle.swift */; }; EAB11531265B437700E11F25 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB11530265B437700E11F25 /* Label.swift */; }; EACD577F25EC694800FEF5A1 /* Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACD577E25EC694800FEF5A1 /* Menu.swift */; }; + EAD48366265F6BC800CBDA06 /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD48365265F6BC800CBDA06 /* Link.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -177,6 +178,7 @@ 7D2D4D6A25148269000F5DDC /* RoundedRectangle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedRectangle.swift; sourceTree = ""; }; EAB11530265B437700E11F25 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; EACD577E25EC694800FEF5A1 /* Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Menu.swift; sourceTree = ""; }; + EAD48365265F6BC800CBDA06 /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -381,6 +383,7 @@ 2CD19FF72512066D00E2CCBA /* Slider.swift */, 2CD19FF62512066D00E2CCBA /* TextField.swift */, 2CD19FF52512066D00E2CCBA /* Toggle.swift */, + EAD48365265F6BC800CBDA06 /* Link.swift */, ); path = Controls; sourceTree = ""; @@ -628,6 +631,7 @@ 2CD1A0462512066D00E2CCBA /* VStack.swift in Sources */, 2CD1A01B2512066D00E2CCBA /* EnvironmentObject.swift in Sources */, 2CD1A0422512066D00E2CCBA /* ScrollView.swift in Sources */, + EAD48366265F6BC800CBDA06 /* Link.swift in Sources */, 2CD1A0442512066D00E2CCBA /* Group.swift in Sources */, 2CD1A0272512066D00E2CCBA /* ViewPropertyViewTypes.swift in Sources */, 2CD1A0132512066D00E2CCBA /* UIViewBuilder.swift in Sources */, diff --git a/Example/AltSwiftUIExample.xcodeproj/project.pbxproj b/Example/AltSwiftUIExample.xcodeproj/project.pbxproj index a8d565f..cf1fe76 100644 --- a/Example/AltSwiftUIExample.xcodeproj/project.pbxproj +++ b/Example/AltSwiftUIExample.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 682C3A7C2594B18A005E798E /* SecureFieldExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 682C3A7B2594B18A005E798E /* SecureFieldExampleView.swift */; }; EAB11538265B6C2E00E11F25 /* LebelExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB11537265B6C2E00E11F25 /* LebelExampleView.swift */; }; EACD577925EC689200FEF5A1 /* MenuExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACD577825EC689200FEF5A1 /* MenuExampleView.swift */; }; + EAD483AD266086E200CBDA06 /* LinkExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD483AC266086E200CBDA06 /* LinkExampleView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -106,6 +107,7 @@ 682C3A7B2594B18A005E798E /* SecureFieldExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureFieldExampleView.swift; sourceTree = ""; }; EAB11537265B6C2E00E11F25 /* LebelExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LebelExampleView.swift; sourceTree = ""; }; EACD577825EC689200FEF5A1 /* MenuExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuExampleView.swift; sourceTree = ""; }; + EAD483AC266086E200CBDA06 /* LinkExampleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkExampleView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -159,6 +161,7 @@ 2C74075D262833D600FF4380 /* ScrollViewTextFieldExample.swift */, 2C7407632628365100FF4380 /* ListTextFieldExampleView.swift */, EAB11537265B6C2E00E11F25 /* LebelExampleView.swift */, + EAD483AC266086E200CBDA06 /* LinkExampleView.swift */, 2C49E3CB2535382F00543E7D /* TextExampleView.swift */, 3ED3C29425F9CE5700F0F602 /* TextFieldExampleView.swift */, ); @@ -388,6 +391,7 @@ 682C3A7C2594B18A005E798E /* SecureFieldExampleView.swift in Sources */, EAB11538265B6C2E00E11F25 /* LebelExampleView.swift in Sources */, 35D1A58825C7EDA100861DC4 /* AlertsExampleView.swift in Sources */, + EAD483AD266086E200CBDA06 /* LinkExampleView.swift in Sources */, 2C83ED522554E50400C378DC /* NavigationExampleView.swift in Sources */, 2C74075E262833D600FF4380 /* ScrollViewTextFieldExample.swift in Sources */, 2CADC30F251CA0DF00EA3F17 /* RamenExampleView.swift in Sources */, diff --git a/Example/AltSwiftUIExample/ExampleViews/LinkExampleView.swift b/Example/AltSwiftUIExample/ExampleViews/LinkExampleView.swift new file mode 100644 index 0000000..c5b78e1 --- /dev/null +++ b/Example/AltSwiftUIExample/ExampleViews/LinkExampleView.swift @@ -0,0 +1,30 @@ +// +// LinkExampleView.swift +// AltSwiftUIExample +// +// Created by Chan, Chengwei on 2021/05/28. +// Copyright © 2021 Rakuten Travel. All rights reserved. +// + +import AltSwiftUI + +struct LinkExampleView: View { + var viewStore = ViewValues() + var body: View { + VStack(spacing: 20) { + Link("View Our Terms of Service", + destination: URL(string: "https://www.example.com/TOS.html")!) + + Link("Link", + destination: URL(string: "https://www.example.com/TOS.html")!) + .font(.headline) + .foregroundColor(.red) + + if #available(iOS 14.0, *) { + Link(destination: URL(string: "https://www.example.com/TOS.html")!) { + Label("Rain", systemImage: "cloud.rain") + } + } + } + } +} diff --git a/Example/AltSwiftUIExample/ViewController.swift b/Example/AltSwiftUIExample/ViewController.swift index 9873e8a..921c605 100644 --- a/Example/AltSwiftUIExample/ViewController.swift +++ b/Example/AltSwiftUIExample/ViewController.swift @@ -34,6 +34,8 @@ struct ExampleView: View { ExampleViewData(title: "Stack Update", destination: StackUpdateExample()), ExampleViewData(title: "Texts", destination: TextExampleView()), ExampleViewData(title: "Label", destination: LabelExampleView()), + ExampleViewData(title: "Link", destination: LinkExampleView()), + ExampleViewData(title: "Ramen Example", destination: RamenExampleView()), ExampleViewData(title: "Ramen Example", destination: RamenExampleView()), ExampleViewData(title: "TextField", destination: TextFieldExampleView()) ] diff --git a/Sources/AltSwiftUI/Source/Views/Controls/Link.swift b/Sources/AltSwiftUI/Source/Views/Controls/Link.swift new file mode 100644 index 0000000..5d437b9 --- /dev/null +++ b/Sources/AltSwiftUI/Source/Views/Controls/Link.swift @@ -0,0 +1,70 @@ +// +// Link.swift +// AltSwiftUI +// +// Created by Chan, Chengwei on 2021/05/21. +// + +import UIKit + +/// A view that can be tapped by the user to open a url link. +public struct Link: View { + public var viewStore = ViewValues() + var label: Label + var url: URL + + /// Creates an instance with a `Text` visual representation. + /// + /// - Parameters: + /// - label: The visual representation of the label + /// - destination: The url for the web site + public init(destination url: URL, label: () -> Label) { + self.label = label() + self.url = url + } + + public var body: View { + self + } +} + +extension Link where Title == Text, Icon == Image { + /// Creates an instance that triggers an `action`. + /// + /// - Parameters: + /// - title: the string of the title lebel + /// - destination: The url for the web site + public init(_ title: S, destination url: URL) where S: StringProtocol { + self.label = Label(title, image: "") + self.url = url + } + + /// Creates an instance that triggers an `action`. + /// + /// - Parameters: + /// - title: the localizedStringKey of the title lebel + /// - destination: The url for the web site + public init(_ title: LocalizedStringKey, destination url: URL) { + self.label = Label(title, image: "") + self.url = url + } +} + +extension Link: Renderable { + public func updateView(_ view: UIView, context: Context) { + guard let view = view as? SwiftUIButton else { return } + + context.viewOperationQueue.addOperation { + label.updateRender(uiView: view.contentView, parentContext: context, drainRenderQueue: false) + } + } + + public func createView(context: Context) -> UIView { + let button = Button { + UIApplication.shared.open(url) + } label: { () -> View in + label + } + return button.createView(context: context) + } +}