Swift: creating UI when function is called - ios

I've got one of these Views here:
let placeContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.whiteColor()
view.translatesAutoresizingMaskIntoConstraints = false
view.layer.cornerRadius = 7
view.layer.masksToBounds = true
return view
}()
And I'm looking to create one next to it every time a function "createNewView()" is called.
let showFullPlaceContainerView = UITapGestureRecognizer(target: self, action: #selector(showFullPlaceContainerViewFunction))
placeContainerView.addGestureRecognizer(showFullPlaceContainerView)
That I want each of the placeContainerView's to respond to.
So I want a view to be generated and I'll give it certain values. And then when I call the function createNewView() I'll get a new view next to it that is exactly the same except with whatever new values I put in.
If you've got any ideas please let me know!
Thank you
EDIT:
The code below demonstrates how I want to setup the placeContainerView each time but I need them to be displayed so that the placeContainerView.topAnchor() is different each time.. How exactly does that work if it is kept in its own class and doesn't know how many times it has been created?
Also, as placeContainerView contains placeLabel and placeImageView do these have to be generated inside the new PlaceContainerViewClass as well?
func setupPlaceContainerView() {
placeContainerView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: -180).active = true
placeContainerView.topAnchor.constraintEqualToAnchor(view.topAnchor, constant: 80).active = true
placeContainerView.widthAnchor.constraintEqualToConstant(227).active = true
placeContainerView.heightAnchor.constraintEqualToConstant(45).active = true
placeContainerView.addSubview(placeLabel)
placeContainerView.addSubview(placeImageLabelSeparator)
placeContainerView.addSubview(placeImageView)
placeLabel.leftAnchor.constraintEqualToAnchor(placeContainerView.leftAnchor).active = true
placeLabel.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeLabel.widthAnchor.constraintEqualToConstant(180).active = true
placeLabel.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true
placeImageLabelSeparator.leftAnchor.constraintEqualToAnchor(placeLabel.rightAnchor).active = true
placeImageLabelSeparator.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeImageLabelSeparator.widthAnchor.constraintEqualToConstant(2).active = true
placeImageLabelSeparator.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true
placeImageView.leftAnchor.constraintEqualToAnchor(placeImageLabelSeparator.rightAnchor).active = true
placeImageView.topAnchor.constraintEqualToAnchor(placeContainerView.topAnchor).active = true
placeImageView.widthAnchor.constraintEqualToConstant(45).active = true
placeImageView.heightAnchor.constraintEqualToAnchor(placeContainerView.heightAnchor).active = true

As noted by #Paulw11, this task is simply done by creating a new class.
class placeContainerView:UIView {
var x:Double!
var y:Bool!
var z:UILabel!
var controller:UIViewController!
//If you want to pass specific values number, you can use convenience init method OR you can use the default init method they give you.
//previousLabelFrame:CGrect = CGRect() // I defaulted all these values to 0, make them whatevree u need. You would use the default one for the first Label you would make. Then after that, you would pass in the previous one made, to get the frame of it so you can add to the one after that.
convenience init(x:Double,y:Bool, z:UILabel, previousLabelFrame:CGRect = CGRect(x: 0, y:0, width:0, height:0), VC:UIViewController) {
self.x = x
self.y = y
self.z = z
self.controller = VC
let distance = self.controller.width*0.1 //Whatever u decide here
//You could just do CGRect(x:previousLabelFrame.maxX+distance, depeding on what you need.
self.frame = CGRect(x: previousLabelFrame.minX+distance, y:previousLabelFrame.minY, width: previousLabelFrame.width, height:previousLabelFrame.height)
}
}
Usage inside ViewController:
var views:[placeContainerView] = []
let view:placeContainerView = placeContainerView(10, true, UILabel(),views[views.count-1], self)
self.views.append(view)
//OR if this is the FIRST placeContainerView of the whole app, it will use the default values for the frame.
let view:placeContainerView = placeContainerView(10, true, UILabel(), self)
self.views.append(view)
Some odd example of how to use.
Then everytime they click a button, just make a new placeContainerView

Related

Swift - access array from another class

In my project I have an array where the user can append elements. My problem right now is that I have another class with a tableView where I want to display all the elements dynamically so if the user adds and element to the array it is also in my tableView.
Class A
var wishListTitlesArray: [String] = [String]()
Class B
var dropDownOptions = [String]() // TableView data -> here I would like to access `wishListTitlesArray`
Update
So thanks for the comments so far, I got the main problem I had but I am still struggling.
My setup:
I create my dropDownButton (which contain a dropDownView) in my ViewController-Class where I also have my var wishListTitlesArray.
I tried dropDownButton.dropView.dropDownOptions = wishListTitlesArray . However that does not do the full job.
#objc func addWishButtonTapped(notification : Notification){
popUpView.popUpTextField.text = ""
self.popUpView.popUpTextField.becomeFirstResponder()
view.addSubview(visualEffectView)
view.addSubview(popUpView)
view.addSubview(wishButton)
self.view.addSubview(dropDownButton)
// constrain blurrEffectView
visualEffectView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
visualEffectView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
visualEffectView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
visualEffectView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
// constrain popUpView
popUpView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
popUpView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
popUpView.heightAnchor.constraint(equalToConstant: 230).isActive = true
popUpView.widthAnchor.constraint(equalToConstant: view.frame.width - 85).isActive = true
// constrain wishButton
wishButton.centerXAnchor.constraint(equalTo: popUpView.centerXAnchor).isActive = true
wishButton.centerYAnchor.constraint(equalTo: popUpView.centerYAnchor, constant: 70).isActive = true
wishButton.heightAnchor.constraint(equalToConstant: 72).isActive = true
wishButton.widthAnchor.constraint(equalToConstant: 72).isActive = true
// constrain DropDownButton
dropDownButton.centerXAnchor.constraint(equalTo: self.popUpView.centerXAnchor).isActive = true
dropDownButton.centerYAnchor.constraint(equalTo: self.popUpView.centerYAnchor).isActive = true
dropDownButton.widthAnchor.constraint(equalToConstant: 100).isActive = true
dropDownButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
// set the drop down menu's options
dropDownButton.dropView.dropDownOptions = wishListTitlesArray
self.view.bringSubviewToFront(visualEffectView)
self.view.bringSubviewToFront(popUpView)
self.view.bringSubviewToFront(wishButton)
self.view.bringSubviewToFront(dropDownButton)
self.view.bringSubviewToFront(dropDownButton.dropView)
This is where the user can add an element to the wishListTitlesArray:
if let txt = listNameTextfield.text {
self.newListTextfield.resignFirstResponder()
// append user-entered text to the data array
self.wishListTitlesArray.append(txt)
self.wishListImagesArray.append(self.image!)
// DonMag3 - append new empty wish array
self.userWishListData.append([Wish]())
let theCustomWishlistView = createCustomWishlistView()
self.view.addSubview(theCustomWishlistView)
// constrain CustomWishlistView
theCustomWishlistView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 120.0).isActive = true
theCustomWishlistView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
theCustomWishlistView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30.0).isActive = true
theCustomWishlistView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30.0).isActive = true
theCustomWishlistView.wishlistImage.image = self.image
theCustomWishlistView.wishlistLabel.text = txt
theCustomWishlistView.transform = CGAffineTransform(translationX: 0, y: 1000)
self.view.bringSubviewToFront(containerView)
// reload the collection view
theCollectionView.reloadData()
theCollectionView.performBatchUpdates(nil, completion: {
(result) in
// scroll to make newly added row visible (if needed)
let i = self.theCollectionView.numberOfItems(inSection: 0) - 1
let idx = IndexPath(item: i, section: 0)
self.theCollectionView.scrollToItem(at: idx, at: .bottom, animated: true)
// close (hide) the "New List" view
self.closeButtonTappedNewList(nil)
})
and the problem right now is that if the user adds an element, dropDownOptions is not updating the data accordingly. So how can I access the updated list? Thanks for all the comments so far!
This is fairly straightforward OO programming. As somebody pointed out, you're dealing with instance variables and instances of classes, not classes.
An analogy:
Instances: If you want one instance of a class to get a value from an instance of another class, it's like having your Toyota car request the radio station presets from your friend's Honda and set your radio the same way.
Classes: The Toyota motor company decides they like the radio presets that Honda uses, so they read the radio station presets from the Honda company and the Toyota factory uses those settings for all new Toyotas.
In class A, make wishListTitlesArray public:
public var wishListTitlesArray: [String] = [String]()
Then, your Class B object will need a pointer to the Class A object. There are various ways to do that. Let's say you set up Class B to take a Class A object at init time:
Class B {
var myAObject: A
var dropDownOptions: [String]
init(aObject: A) {
myAObject = anObject
dropDownOptions = myAObject.wishListTitlesArray
}
//more code here
}

what is the difference between let and var for reference types, when we create an IBOutlet programmatically in ios, swift [duplicate]

This question already has answers here:
What is the difference between `let` and `var` in Swift?
(32 answers)
Closed 5 years ago.
we can define and set constraints programmatically like below in swift. I created four label outlets in four different ways. like below
var labelone : UILabel = {
var label = UILabel()
label.text = "Stack"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labeltwo : UILabel = {
let label = UILabel()
label.text = "Overflow"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var labelthree : UILabel = {
let label = UILabel()
label.text = "Confused"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let labelfour: UILabel = {
var label = UILabel()
label.text = "More confused"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
func addconstaraints() {
labelone.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
labelone.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
labelone.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
labeltwo.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
labeltwo.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
labeltwo.topAnchor.constraint(equalTo: labelone.bottomAnchor).isActive = true
labelthree.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
labelthree.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
labelthree.topAnchor.constraint(equalTo: labeltwo.bottomAnchor).isActive = true
labelfour.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
labelfour.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
labelfour.topAnchor.constraint(equalTo: labelthree.bottomAnchor).isActive = true
}
all are working fine. are there any differences. hope your help to understand this.
I know the difference between let and var for variable define. is it same for the outlets. how can we see the difference in outlet
In your example, you're considering using var vs. let in declaring the property itself, as well as inside the closure that initialized that property. In answer to your question, bottom line, anywhere you can use let (for constants), you theoretically can use var (for variables), but you should only do so if you're planning on changing that variable later. In the case of a reference type, like UILabel, this means if you plan on replacing that label with an entirely new instance of UILabel.
So, the first and fourth options, where var was used inside the closure, can be dismissed out of hand as poor programming style because you're not changing it again within the scope of the closure, so we know we should use let inside the closure. Regarding second or third options (i.e. whether the property, itself, should be constant or variable), the question is whether you're ever going to replace that UILabel with another, later. If so, you have to use the third option. But we can suspect that this was unlikely to be your intent, and so if you don't plan on replacing that label later, of these four options, you would favor the second option of let/let.
Having said that, this looks like this is in a view controller, and I wouldn't advise instantiating any view objects during the instantiation of the view controller. Usually that's deferred to viewDidLoad or if the entire view hierarchy is built programmatically, in loadView. Or, even better, we get out of the business of building controls manually and we let the storyboard instantiate IBOutlet references at the appropriate time.
let:
let keyword in Swift is used to declare constant value and immutable can never be changed once defined.
let name ="Kirit"
After you can change name = "Modi" then you got compiler error. So declare value of its only one time using let keyword.
var :
var keyword is mutable in Swift is used to declare variant value. That value can change at run time. It means can change it too many time.
var name = "Kirit"
You can changed name = "Modi" then successfully updated value of variable.

Create JTAppleCalendarView in code

I want to create and customize a JTAppleCalendarView using only Swif 3 code, i.e. without the Interface Builder, Storyboards or XIB files.
I can create customs labels by code, however when I try to create a JTAppleCalendarView, I simply can not change the frame value, neither the heightAnchor or widthAnchor. This way, my calendar is not displayed.
I am attaching the code of the init of the my custom UIViewController (which implements the Datasource and Delegate protocols):
init(frame: CGRect) {
super.init(nibName: nil, bundle: nil)]
self.view = UIView(frame: frame)
self.view.backgroundColor = UIColor.green
let margins = self.view.layoutMarginsGuide
let calendar = JTAppleCalendarView()
print("CalendarView frame: ", calendar.frame)
calendar.dataSource = self
calendar.delegate = self
calendar.cellInset = CGPoint(x: 0, y: 0)
calendar.backgroundColor = UIColor.white
calendar.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(calendar)
calendar.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
calendar.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
calendar.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true
//calendar.widthAnchor.constraint(equalTo: margins.widthAnchor).isActive = true
calendar.heightAnchor.constraint(equalToConstant: 400).isActive = true
}
Whenever I change calendar.heightAnchor, calendar.widthAnchor or calendar.frame, I get the error "Unexpectedly found nil while unwrapping an Optional value.".
I followed the library tutorial, however they only presents the creation using Interface Builders, which I am not using in my project. Tecnically, the component is just a custom UIView, so I am confused with this error.
In the code above, I forgot to register the cell.
Solved registering the cell with something like: calendar.registerCellViewClass(type: SomeCellClass.self).
So far, it worked for a simple project. I did not tested with playgrounds.

how to copy one view to another view in Swift

I want to copy one UIView to another view without making it archive or unarchive.
Please help me if you have any solution.
I tried with by making an extension of UIView as already available an answer on Stack over flow. But its crashing when I pass the view with pattern Image Background color.
The code related to my comment below:
extension UIView
{
func copyView() -> UIView?
{
return NSKeyedUnarchiver.unarchiveObjectWithData(NSKeyedArchiver.archivedDataWithRootObject(self)) as? UIView
}
}
I've just tried this simple code in a Playground to check that the copy view works and it's not pointing the same view:
let originalView = UIView(frame: CGRectMake(0, 0, 100, 50));
originalView.backgroundColor = UIColor.redColor();
let originalLabel = UILabel(frame: originalView.frame);
originalLabel.text = "Hi";
originalLabel.backgroundColor = UIColor.whiteColor();
originalView.addSubview(originalLabel);
let copyView = originalView.copyView();
let copyLabel = copyView?.subviews[0] as! UILabel;
originalView.backgroundColor = UIColor.blackColor();
originalLabel.text = "Hola";
originalView.backgroundColor; // Returns black
originalLabel.text; // Returns "Hola"
copyView!.backgroundColor; // Returns red
copyLabel.text; // Returns "Hi"
If the extension wouldn't work, both copyView and originalView would have same backgroundColor and the same would happen to the text of the labels. So maybe there is the possibility that the problem is in other part.
Original Post
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.hidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotViewAfterScreenUpdates(true)
viewforCopy.hidden = true
return viewCopy
}
Updated for Swift 4
func copyView(viewforCopy: UIView) -> UIView {
viewforCopy.isHidden = false //The copy not works if is hidden, just prevention
let viewCopy = viewforCopy.snapshotView(afterScreenUpdates: true)
viewforCopy.isHidden = true
return viewCopy!
}

Accessibility Voice Over loses focus on Segmented SubView

I'm working on an Accessibility project where I have a segmentedController in the NavigationBar. Almost everything is working fine until the focus comes at the middle (2/3) SegmentedController. It won't speak the the accessibilityLabel..
See my code.
I'm using NSNotifications to let the 'UIAccessibilityPostNotification' know when to focus:
func chatLijst() {
let subViews = customSC.subviews
let lijstView = subViews.last as UIView
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, lijstView)
}
func berichtenLijst() {
let subViews = customSC.subviews
let messageView = subViews[1] as UIView
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, messageView)
}
func contactenLijst() {
let subViews = customSC.subviews
let contactenView = subViews.first as UIView
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, contactenView)
}
func setupSegmentedController(){
let lijst:NSString = "Lijst"
lijst.isAccessibilityElement = false
lijst.accessibilityLabel = "Lijst met gesprekken"
let bericht:NSString = "Bericht"
bericht.isAccessibilityElement = false
bericht.accessibilityLabel = "Bericht schrijven"
let contacten:NSString = "Contacten"
contacten.isAccessibilityElement = false
contacten.accessibilityLabel = "Contacten opzoeken"
let midden:CGFloat = (self.view.frame.size.width - 233) / 2
customSC.frame = CGRectMake(midden, 7, 233, 30)
customSC.insertSegmentWithTitle(lijst, atIndex: 0, animated: true)
customSC.insertSegmentWithTitle(bericht, atIndex: 1, animated: true)
customSC.insertSegmentWithTitle(contacten, atIndex: 2, animated: true)
customSC.selectedSegmentIndex = 0
customSC.tintColor = UIColor.yellowColor()
customSC.isAccessibilityElement = true
self.navigationController?.navigationBar.addSubview(customSC)
}
Fix
Strange enough I had to restructure the subViews array in the setup func and replace UIAccessibilityPostNotification object with the new segmentsView array.
func chatLijst() {
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, segmentsViews[0])
}
// Restructure subviews....
segmentsViews = [customSC.subviews[2], customSC.subviews[1], customSC.subviews[0]]
I'm using NSNotifications to let the 'UIAccessibilityPostNotification' know when to focus
Don't. That's a poor way to build a custom accessible control, and more importantly it can be confusing to the user. The screen changed notification doesn't just change focus, it also plays a specific sound that indicates to the user that the contents of the screen has changed.
Instead, I would recommend that you either make the subviews that you want appear as accessibility elements be accessibility elements with their own labels and traits and then rely on the OS to focus and activate them, or that you implement the UIAccessibilityContainer protocol in your custom control and then rely on the OS to focus and activate them.

Resources