change text in arkitview via button(swift4) - button

My code uses arkit. All I want to do is when I change the text of the label. The change is reflected in the arkit text. Currently its not. Whatever the arkit label stays and default and never changes no matter what is on the label on the view controller.
import UIKit;import ARKit
class ViewController: UIViewController {
#IBOutlet var arkitView:ARSCNView!
#IBOutlet var outputImageView:UIImageView!
#IBOutlet var ffox: UILabel!
#IBOutlet var enterText: UITextField!
var textGeometry: SCNText!
//4. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//5. Create Our Session
let augmentedRealitySession = ARSession()
override func viewDidAppear(_ animated: Bool) {
arkitView.session.run(configuration)
//1. Create The Text
textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)
//2. Set It From The UILabel
textGeometry.string = ffox.text!
//3. Create A Material For The Text
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
textGeometry.materials = [material]
//4. Create A Holder Node To Hold The Text
let node = SCNNode()
node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
node.geometry = textGeometry
arkitView.scene.rootNode.addChildNode(node)
arkitView.automaticallyUpdatesLighting = true
DispatchQueue.main.async {
self.textGeometry.string = self.ffox.text!
}
}
#IBAction func takeScreenshotAction() {
if let d = enterText.text
{
ffox.text = d
}}}

Looking at your code you may want to set the Font and Font Size e.g:
textGeometry.font = UIFont(name: "Helvatica", size: 3)
Changing The Text:
To change the text of SCNText you need to access it's string property.
Declare you SCNText as a var above your Class Definition e.g.
let textGeometry: SCNText!
And personally I would initialize it in ViewDidLoad.
Then to change it you simply call:
textGeometry.string = "I Have Changed The String"
So in your example assuming as I said that your SCNText is called textGeometry you can do something like this:
textGeometry.string = ffox.text
An Example Of Creating An SCNText Geometry:
class Text: SCNNode{
var textGeometry: SCNText!
/// Creates An SCNText Geometry
///
/// - Parameters:
/// - text: String (The Text To Be Displayed)
/// - depth: Optional CGFloat (Defaults To 1)
/// - font: UIFont
/// - textSize: Optional CGFloat (Defaults To 3)
/// - colour: UIColor
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
//1. Create The Text Geometry With String & Depth Parameters
textGeometry = SCNText(string: text , extrusionDepth: depth)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: font, size: textSize)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = colour
//5. Set The Text's Material
self.geometry = textGeometry
//6. Scale The Text So We Can Actually See It!
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Using This Class In Your Scene:
//1. Create An SCNText With A White Colour
let nodeToAdd = Text(text: "Hello AR", colour: .white)
//2. Add The Text To The Scene
augmentedRealityView.scene.rootNode.addChildNode(nodeToAdd)
//3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
nodeToAdd.position = SCNVector3(0, 0, -1.5)
Hope this helps...
Update:
The code above works perfectly however since your are having trouble implementing it, here is how you code should look:
class Example: UIViewController {
//1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
#IBOutlet weak var augmentedRealityView: ARSCNView!
//2. Create A Label To Display Text
#IBOutlet weak var ffox: UILabel!
//3. Create The Text Gemoetry
var textGeometry: SCNText!
//4. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//5. Create Our Session
let augmentedRealitySession = ARSession()
//--------------------
//MARK: View LifeCycle
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
augmentedRealityView.session.run(configuration)
//1. Create The Text
textGeometry = SCNText(string: ffox.text!, extrusionDepth: 1)
//2. Set It From The UILabel
textGeometry.string = ffox.text!
//3. Create A Material For The Text
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
textGeometry.materials = [material]
//4. Create A Holder Node To Hold The Text
let node = SCNNode()
node.position = SCNVector3(x: 0, y: 0.02, z: -0.01)
node.scale = SCNVector3(x: 0.01, y: 0.01, z: 0.01)
node.geometry = textGeometry
augmentedRealityView.scene.rootNode.addChildNode(node)
augmentedRealityView.automaticallyUpdatesLighting = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I expect the reason your app is crashing is perhaps you have not setup the ffox text in Interface Builder and Wired it up as in the image below:
Also remember the UILabel must contain text!
Final Update:
Since Sam has still not been able to get this to work, I am writing a more in-depth example, which does the following:
(1) The text in a UILabel will be displayed in an SCNTextGeometry on Load.
(2) You can change the string of the SCNTextGeometry by pressing one of three buttons,
(3) You can change the string of the SCNTextGeometry by using a UITextField Input.
All of this has been fully tested and works as does my other example.
You can download the full example here: Sample Project
And of course the `ViewController' Class:
import UIKit
import ARKit
//--------------------
//MARK: TextNode Class
//--------------------
class TextNode: SCNNode{
var textGeometry: SCNText!
/// Creates An SCNText Geometry
///
/// - Parameters:
/// - text: String (The Text To Be Displayed)
/// - depth: Optional CGFloat (Defaults To 1)
/// - font: UIFont
/// - textSize: Optional CGFloat (Defaults To 3)
/// - colour: UIColor
init(text: String, depth: CGFloat = 1, font: String = "Helvatica", textSize: CGFloat = 3, colour: UIColor) {
super.init()
//1. Create The Text Geometry With String & Depth Parameters
textGeometry = SCNText(string: text , extrusionDepth: depth)
//2. Set The Font With Our Set Font & Size
textGeometry.font = UIFont(name: font, size: textSize)
//3. Set The Flatness To Zero (This Makes The Text Look Smoother)
textGeometry.flatness = 0
//4. Set The Colour Of The Text
textGeometry.firstMaterial?.diffuse.contents = colour
//5. Set The Text's Material
self.geometry = textGeometry
//6. Scale The Text So We Can Actually See It!
self.scale = SCNVector3(0.01, 0.01 , 0.01)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//--------------------------
//MARK: UITextFieldDelegate
//--------------------------
extension ViewController: UITextFieldDelegate{
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
print("Current Text = \(textField.text!)")
DispatchQueue.main.async {
self.textNode.textGeometry.string = textField.text!
}
return true
}
}
class ViewController: UIViewController {
//1. Create A Reference To Our ARSCNView In Our Storyboard Which Displays The Camera Feed
#IBOutlet weak var augmentedRealityView: ARSCNView!
//2. Create A Reference To Our SCNNode
var textNode: TextNode!
//2. Create A Label To Display Text
#IBOutlet weak var ffox: UILabel!
//3. Create UITextField So Text Can Be Changed Dynamically
#IBOutlet weak var textChangerField: UITextField!
//3. Create Our ARWorld Tracking Configuration
let configuration = ARWorldTrackingConfiguration()
//3. Create Our Session
let augmentedRealitySession = ARSession()
//--------------------
//MARK: View LifeCycle
//--------------------
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
augmentedRealityView.session.run(configuration)
//1. Create An SCNText With A White Colour & With The Label's Text
textNode = TextNode(text: ffox.text!, colour: .white)
//2. Add The Text To The Scene
augmentedRealityView.scene.rootNode.addChildNode(textNode)
//3. Set The Text's Position Center On The Screen & 1.5m In Front Of The Camera
textNode.position = SCNVector3(0, 0, -1.5)
//4. Set The Lighting
augmentedRealityView.automaticallyUpdatesLighting = true
//5. Assign The Delegate To The TextChangerField
textChangerField.delegate = self
}
/// Changes The Text In The Button At The Bottom Of The Screen
///
/// - Parameter sender: UIButton
#IBAction func changeText(_ sender: UIButton){
guard let textToDisplay = sender.titleLabel?.text else { return }
DispatchQueue.main.async {
self.textNode.textGeometry.string = textToDisplay
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
In Interface Builder you will have the following:

Related

SwiftUI - share dictionary among views, unclear what arguments to use at #Main / WindowGroup

I'm trying to build an app (macOS, but would be the same for iOS) that creates a number of grids, the outcome of which is to be shown in a second screen. For this, I'm sharing data across these screens, and I'm running into an issue here, I hope someone can help or point me in the right direction. I'll share a simplified version of the code below (working in Xcode 14.0.1)
The code creates a dictionary that can be shown in a grid, on which calculations can be done. The idea is then to add this grid, with some descriptive variables, into another dictionary
The building blocks of the grid are cells
Import Foundation
struct Cell: Comparable, Equatable, Identifiable, Hashable {
static func == (lhs: Cell, rhs: Cell) -> Bool {
lhs.randomVarOne == rhs.randomVarOne
}
var randomVarOne: Double
var randomVarTwo: Bool
// other vars omitted here
var id: Int { randomVarOne }
static func < (lhs: Cell, rhs: Cell) -> Bool {
return lhs.randomVarOne < rhs.randomVarOne
}
}
this is also where there are a bunch of funcs to calculate next neighbor cells in the grid etc
then the grid is defined in a class:
class Info: ObservableObject, Hashable {
static func == (lhs: Info, rhs: Info) -> Bool {
lhs.grid == rhs.grid
}
func hash(into hasher: inout Hasher) {
hasher.combine(grid)
}
#Published var grid = [Cell]()
var arrayTotal = 900
#Published var toBeUsedForTheGridCalculations: Double = 0.0
var toBeUsedToSetTheVarAbove: Double = 0.0
var rowTotalDouble: Double {sqrt(Double(arrayTotal)) }
var rowTotal: Int {
Int(rowTotalDouble) != 0 ? Int(rowTotalDouble) : 10 }
The class includes a func to create and populate the grid with Cells and add these Cells to the grid var. It also includes the formulas to do the calculations on the grid using a user input. The class did not seem to need an initializer.
This is the Scenario struct:
struct Scenario: Comparable, Equatable, Identifiable, Hashable {
static func == (lhs: Scenario, rhs: Scenario) -> Bool {
lhs.scenarioNumber == rhs.scenarioNumber
}
func hash(into hasher: inout Hasher) {
hasher.combine(scenarioNumber)
}
var scenarioNumber: Int
var date: Date
var thisIsOneSnapshot = [Info]()
var id: Int { scenarioNumber }
static func < (lhs: Scenario, rhs: Scenario) -> Bool {
return lhs.scenarioNumber < rhs.scenarioNumber
}
}
added hashable since it uses the Info class as an input.
Then there is the class showing the output overview
class OutputOverview: ObservableObject {
#Published var snapshot = [Scenario]()
// the class includes a formula of how to add the collection of cells (grid) and the additional variables to the snapshot dictionary. Again no initializer was necessary.
Now to go to the ContentView.
struct ContentView: View {
#Environment(\.openURL) var openURL
var scenarioNumberInput: Int = 0
var timeStampAssigned: Date = Date.now
#ObservedObject private var currentGrid: Info = Info()
#ObservedObject private var scenarios: Combinations = Combinations()
var usedForTheCalculations: Double = 0.0
var rows =
[
GridItem(.flexible()),
// whole list of GridItems, I do not know how to calculate these:
// var rows = Array(repeating: GridItem(.flexible()), count: currentGrid.rowTotal)
//gives error "Cannot use instance member 'currentGrid' within property initializer;
// property iunitializers run before 'self' is available
]
var body: some View {
GeometryReader { geometry in
VStack {
ScrollView {
LazyHGrid(rows: rows, spacing: 0) {
ForEach(0..<currentGrid.grid.count, id :\.self) { w in
let temp = currentGrid.grid[w].varThatAffectsFontColor
let temp2 = currentGrid.grid[w].varThatAffectsBackground
Text("\(currentGrid.grid[w].randomVarOne, specifier: "%.2f")")
.frame(width: 25, height: 25)
.border(.black)
.font(.system(size: 7))
.foregroundColor(Color(wordName: temp))
.background(Color(wordName: temp2))
}
}
.padding(.top)
}
VStack{
HStack {
Button("Start") {
}
// then some buttons to do the calculations
Button("Add to collection"){
scenarios.addScenario(numbering: scenarioNumberInput, timeStamp:
Date.now, collection: currentGrid.grid)
} // this should add the newly recalculated grid to the dictionary
Button("Go to Results") {
guard let url = URL(string: "myapp://scenario") else { return }
openURL(url)
} // to go to the screen showing the scenarios
Then the second View, the ScenarioView:
struct ScenarioView: View {
#State var selectedScenario = 1
#ObservedObject private var scenarios: OutputOverview
var pickerNumbers = [ 1, 2, 3, 4 , 5]
// this is to be linked to the number of scenarios completed,this code is not done yet.
var rows =
[
GridItem(.flexible()),
GridItem(.flexible()),
// similar list of GridItems here....
var body: some View {
Form {
Section {
Picker("Select a scenario", selection: $selectedScenario) {
ForEach(pickerNumbers, id: \.self) {
Text("\($0)")
}
}
}
Section {
ScrollView {
if let idx = scenarios.snapshot.firstIndex(where:
{$0.scenarioNumber == selectedScenario}) {
LazyHGrid(rows: rows, spacing: 0) {
ForEach(0..<scenarios.snapshot[idx].thisIsOneSnapshot.count,
id :\.self) { w in
let temp =
scenarios.snapshot[idx].thisIsOneSnapshot[w].varThatAffectsFontColor
let temp2 =
scenarios.snapshot[idx].thisIsOneSnapshot[w].varThatAffectsBackground
Text("\(scenarios.snapshot[idx].thisIsOneSnapshot[w].randomVarOne, specifier: "%.2f")")
.frame(width: 25, height: 25)
.border(.black)
.font(.system(size: 7))
.foregroundColor(Color(wordName: temp))
.background(Color(wordName: temp2))
}
}
}
}
}
}
}
}
Now while the above does not (for the moment..) give me error messages, I am not able to run the PreviewProvider in the second View. The main problem is in #main:
import SwiftUI
#main
struct ThisIsTheNameOfMyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.handlesExternalEvents(matching: ["main"])
WindowGroup("Scenarios") {
ScenarioView()
// error messages here: 'ScenarioView' initializer is inaccessible due to "private"
// protection level - I don't know what is set to private in ScenarioView that could
// cause this
// second error message: missing argument for parameter 'scenarios' in call
}
.handlesExternalEvents(matching: ["scenario"])
}
}
I am at a loss on how to solve these 2 error messages and would be very grateful for any tips or guidance. Apologies if this question is very long, I scanned many other forum questions and could not find any good answers.
I have tried adding pro forma data in #main as follows
#main
struct FloodModelScenarioViewerApp: App {
#State var scenarios = Scenario(scenarioNumber: 1, date: Date.now)
var body: some Scene {
WindowGroup {
ContentView()
}
.handlesExternalEvents(matching: ["main"])
WindowGroup("Scenarios") {
ScenarioView(scenarios: scenarios)
}
.handlesExternalEvents(matching: ["scenario"])
}
}
This still gives 2 error messages:
same issue with regards to ScenarioView initialiser being inaccessible due to being 'private'
Cannot convert value of type 'Scenario' to expected argument type 'OutputOverview'
Just remove the private from
#ObservedObject private var scenarios: OutputOverview
The value is coming from he parent so the parent needs access. So put
#StateObject private var scenarios: OutputOverview = .init()
in FloodModelScenarioViewerApp
#StateObject is for initializing ObservableObjects and #ObservedObject is for passing them around.
I don't know if your code will work after you read this question, and that's because there are many things to correct, but you can start with these:
In Cell, you shouldn't use an id that is a variable, this may cause inconsistent behavior. Use something like:
let id = UUID()
When you initialize ContentView, you can't use currentGrid inside a variable because currentGrid will not be available before all variables are initialized. Meaning, you are trying to initialize rows before currentGrid actually exists. You can try using the .onAppear modifier:
var rows = [GridItem]()
var body: some View {
GeometryReader { geometry in
// ... view code in here
}
.onAppear {
var rows = Array(repeating: GridItem(.flexible()), count: currentGrid.rowTotal)
}
}
This creates the view and, before showing it, the grid is set to its proper value.
The message 'ScenarioView' initializer is inaccessible due to "private" protection level seems clear: you must provide a value to to the variable scenarios (it doesn't have a default value) but it's marked as private. Remove private.
#ObservedObject var scenarios: OutputOverview
Then, remember to pass a value of type OutputOverview for the variable when you call the view:
ScenarioView(scenarios: aVariableOfTypeOutputOverview)
The type mismatch error you get inside the #main code is also clear - you have defined a variable of type Scenario:
#State var scenarios = Scenario(scenarioNumber: 1, date: Date.now)
but ScenarioView requires another type:
#ObservedObject private var scenarios: OutputOverview
One of them needs change for your code to work.

How to fix "cannot use instance member 'textField_1' within property initializer; property initializers run before 'self' is available"?

My compiler doesn't show any problems but at runtime, I get an unknown error. I'm trying to place textField string input into a double array. Not sure what is wrong can somebody please explain what I'm doing wrong.
Haven't really tried anything but I think I have to use lazy syntax somehow.
import UIKit
class ViewController: UIViewController {
// Outlets
// textFields
#IBOutlet weak var textField_1: UITextField!
#IBOutlet weak var textField_2: UITextField!
#IBOutlet weak var textField_3: UITextField!
#IBOutlet weak var textField_4: UITextField!
#IBOutlet weak var textField_5: UITextField!
// labels
#IBOutlet weak var label_1: UILabel!
#IBOutlet weak var label_2: UILabel!
#IBOutlet weak var label_3: UILabel!
#IBOutlet weak var label_4: UILabel!
#IBOutlet weak var label5: UILabel!
// Variables
var input : String = "0";
var a = 0
var age = "9.5"
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
var numbers : [Double] = [90, 0, 0, 0, 0]; // where textfields will be converted to double
var userinput : [String?] = [textField_1.text, textField_2.text, textField_3.text, textField_4.text, textField_5.text]!; // textField storage
// Button
#IBAction func button ()
{
// loop inserts userinput into numbers array
while (a < 3) {
numbers.insert(convert_to_double(input1: userinput[a]!) ,at: a)// inserts userinput after
a += 1 // stops loop and changes array index
}
}
// converts strings to double
func convert_to_double(input1: String) -> Double
{
let input0 : Double? = Double(input1);
return input0!
}
}
:0: error: cannot use instance member 'textField_1' within property initializer; property initializers run before 'self' is availableenter image description here
You are trying to access the user's input with an index value. Creating an array of text input values has two problems:
In your case, you can't create this array with a property initializer because self is not available until after the class has been fully initialized.
Creating an array of the user inputs will be static and won't show the latest values of the UITextFields if the user updates one before pressing the button.
You can handle this in one of two ways.
Use a computed property:
If you turn userinput into a computed property, then it will always return an array with the latest values:
var userinput : [String?] { return [textField_1.text, textField_2.text, textField_3.text, textField_4.text, textField_5.text] }
The downside is that every access of userinput will recreate this array which is a bit inefficient, but it ensures the array contains the latest values. You can assign this to a local variable at the top of button to avoid recreating the array in the loop:
#IBAction func button ()
{
let userinput = self.userinput
// loop inserts userinput into numbers array
while (a < 3) {
numbers.insert(convert_to_double(input1: userinput[a]!) ,at: a)// inserts userinput after
a += 1 // stops loop and changes array index
}
}
Create a lazy var of the UITextFields:
Create a lazy array of the textFields. This array will be created the first time it is accessed, but since it contains objects, you will always have access to the latest input values from the user.
lazy var textFields = [textField_1!, textField_2!, textField_3!, textField_4!, textField_5!]
and then change your button loop to use textFields[a].text! instead of userinput[a]!:
#IBAction func button ()
{
// loop inserts userinput into numbers array
while (a < 3) {
numbers.insert(convert_to_double(input1: textFields[a].text!) ,at: a)// inserts userinput after
a += 1 // stops loop and changes array index
}
}

Corner Radius Not Working?

im trying to setup a circle image view and when I set the corner radius to perform the operation it does absolutely nothing. I've looked at various threads and solutions none worked
import UIKit
class AlterProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view?.backgroundColor = UIColor.white
navigationItem.title = "Profile Settings"
view.addSubview(selectProfileImage)
///Constraints for all views will go here
_ = selectProfileImage.anchor(view.centerYAnchor, left: view.leftAnchor, bottom: nil, right: nil, topConstant: -275, leftConstant: 135, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 100)
// selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width/2
///////////////////////////////////////////////
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//Where all buttons and labels will be added
//will just be a nice looking image view to be next to the profile settings button
lazy var selectProfileImage: UIImageView = {
let selectPicture = UIImageView()
// self.selectProfileImage.layer.cornerRadius = self.selectProfileImage.frame.size.width / 2;
selectPicture.image = UIImage(named: "Paris")
// selectPicture.layer.cornerRadius = selectPicture.frame.size.width / 2;
selectPicture.clipsToBounds = true
selectPicture.translatesAutoresizingMaskIntoConstraints = false
selectPicture.contentMode = .scaleAspectFill
selectPicture.layer.shouldRasterize = true
selectPicture.layer.masksToBounds = true
return selectPicture
}()
///////////////////////////////////////////////////////////////////////////////////
}
None of the methods seem to work im actually kind of stumped right now
Given that you layout with AutoLayout I would suspect the image view simply doesn't have the correct size when you calculate the radius. The image view is initialized with a size of 0,0 and thus the calculated radius will be 0 as well. Instead, move the radius calculation in viewDidLayoutSubviews after calling super:
func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width / 2;
selectProfileImage.layer.masksToBounds = true
}

how to make button (in annotationView map) open different links on each new pin

*Question is : How can i make the button (when pressed) open the link witch corresponds to the chosen shop on the pin?
I have an array of shops (for pins) (i created a class for it, and a class for pinAnnotation)
here i am creating button and pin image
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "annotationReusedId"
var anView = myMapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
} else {
anView!.annotation = annotation
}
anView!.image = UIImage(named: "upcycled")
anView?.frame = CGRect(x: 0, y: 0, width: 35, height: 35)
anView!.backgroundColor = UIColor.clear
anView!.canShowCallout = true
let rightButton = UIButton(type: .infoLight)
rightButton.tag = annotation.hash
rightButton.addTarget(self, action:#selector(openLinkInstagram), for: .touchUpInside)
//anView!.animatesDrop = true
anView!.canShowCallout = true
anView!.rightCalloutAccessoryView = rightButton
return anView
}
here is func for openning link
func openLinkInstagram() {
}

What is wrong with my constraints?

I am simply trying to create a view with a contained view that is 50% as tall and high. In the following code, I set constraints to achieve that when I add the view to the super-view. I set translatesAutoResizingMaskToConstraints to false, add the constraints via anchors. I also tried to call setNeedsUpdateConstraints, and add the same constraints in updateConstraints.
But in the following Playground code, I don't see the constrained subview testView. I expect to see an blue view half the size of the orange view, but all i see is orange.
I'm not sure what I am missing here.
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class LView: UIView {
var testView = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .orange
setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
addSubview(testView)
testView.backgroundColor = .blue
testView.text = "....."
//testView.setNeedsUpdateConstraints()
testView.translatesAutoresizingMaskIntoConstraints = false
testView.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.5).isActive = true
testView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.5).isActive = true
testView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
testView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
}
}
let testView = LView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
PlaygroundPage.current.liveView = testView

Resources