I would like to create multiple UIViews that can be reproduced by using a single function. I have a UIView that is placed on a storyboard and connected to my class with an IBOutlet:
#IBOutlet weak var contentView: UIView!
I have a function that loads a xib into my UIView:
func createView(layoutConstant: CGFloat) {
if let customView = NSBundle.mainBundle().loadNibNamed("TestView", owner: self, options: nil).first as? TestView {
contentViewTopLayoutConstraint.constant = layoutConstant
contentView.addSubview(customView)
}
}
I am now trying to add two of them to my view, but only one shows up:
createView(0)
createView(70)
Any ideas?
I think both views are added, although they happen to be in the same spot, so it looks like there is only one! A quick and dirty way to verify that would be updating your createView method with this line:
contentView.frame.origin.y = layoutConstant
Basically your contentViewTopLayoutConstraint is not connected to the views you are creating, so setting its constant value will not have any impact.
Because frames for all those views will of same size. Origin(x,y) will be same for all the views, so they are overlapping one on another and you can only see the top one view.
In your code example it looks like you're only setting a layout constraint on the contentView you are placing your two new views inside of. What you need to do is set layout constraints on the two views your are placing inside in relation to their superview i.e. the contentView.
Basically, add the layout constraints to the customView views.
its quite simple.. iterate a loop by creating uiview along with adding those into the array and customize your particular view by getting them using array index.
Happy code ..
Related
Recently I wrote an app with one single scene and ViewController. I had to set a custom background picture for the View, which the ViewController manages (i.e. my top view contained the UIImageView). Later on I had to implement some logic in ViewController, so that it properly rotates/changes the picture when the screen is rotated. Also I had to overwrite some properties like preferredStatusBarStyle for the ViewController.
Now I have to implement a couple more scenes / screens in my app and it turns out that they all must have the same design as this currently present screen, so I think it makes sense if I create a CommonViewController which contains this common rotation-related logic for background picture, so that I can inherit all my other ViewControllers from this CommonViewController. The only problem I have is that CommonViewController "requires" that the view it manages has a backgroundPicture: UIView property, which I don't know how to ensure.
If I create a new file CommonViewController together with XIB-file, I can add the backgroundPicture image view in XIB and connect it with code (via regular "control-drag" approach), but apparently this won't work, as there is no guarantee that the views which inherit CommonViewController will have this property. What is the correct way to solve this issue without hacks on iOS in Swift?
Unfortunately I could not find a solution, maybe I've been searching for something wrong. It seems that I somehow need to inherit a CommonViewController for each scene (for each CustomViewController), but also I have to somehow set the top view of each of these controller's to be equal to some CommonView, so that CommonViewController does not crash when I try to access #IBOutlet weak var backgroundPicutre: UIImageView!.
The obvious way would be to define some method or property in the CommonViewController, so that the controllers which inherit it, can implement / override it, but it seems a bit hacky as it still requires copy-pasting in each ViewController which inherits CommonViewController.
How I imagined the solution: I create CustomViewController: CommonViewController, then I create a view controller in my Storyboard and change the "Class" property to "CustomViewController" (in property editor), then I select the view which corresponds to this newly added controller and change the "Class" property to "BackgroundImageView. But I'm not sure if it's the correct way to do (also I doubt thatCustomViewControllerwill properly "connect" itsIBOutletfieldbakcgroundImageViewwith the correspondingUIViewfromBackgroundImageView`, that's why I wanted to ask experts what they think about it.
I think you should define your base controller (CommonViewController) entirely in code, i.e. don't use no xibs / storyboards for the base controller. It doesn't mean you should rid off storyboards / xibs completely. Interface for alll other view controllers except CommonViewController may still be implemented with xibs / storyboards.
In this case CommonViewController implementation may look like this:
import UIKit
class CommonViewController: UIViewController {
// use this property every time you need to
// manipulate backgroundPicture
var backgroundPicture: UIImageView = {
// Replace with your image name
let image = UIImage(named: "BackgroundPicture")!
let imageView = UIImageView()
imageView.image = image
return imageView
}()
override func viewDidLoad() {
// If subclass overrides viewDidLoad()
// it should contain super.viewDidLoad()
super.viewDidLoad()
view.addSubview(backgroundPicture)
// Align backgroundPicture to bounds of superview
// You can remove this code and implement
// your own alignment with frames or Autolayout
backgroundPicture.frame = view.bounds
// Send backgroundPicture to back of the view
// Otherwise backgroundPicture may overlap views added in subclasses
view.sendSubviewToBack(backgroundPicture)
}
override func viewDidLayoutSubviews() {
// If subclass overrides viewDidLayoutSubviews()
// It should contain super.viewDidLayoutSubviews()
super.viewDidLayoutSubvews()
// Align backgroundPicture to bounds of superview
// You can remove this code and implement
// your own alignment with frames or Autolayout
backgroundPicture.frame = view.bounds
}
}
I'm facing one issue while updating top constraint dynamically.
I have one subview1 added over viewcontroller1 view in its xib file and i have given topview constraint to subview1 as 65 and created an outlet for it.
Now i have added viewcontroller1 view over viewcontroller2. After adding it i'm trying to update the constant value of the constraint to 108. But its not getting reflected.
In viewcontroller1 i'm doing
self.topErrorViewConstarints.constant = 108
self.view.updateConstraints()
Any idea why its not getting reflected?
You need to layout the view again.
self.view.layoutIfNeeded()
Try this:
self.topErrorViewConstarints.constant = 108
self.view.setNeedsUpdateConstraints()
self.view.layoutIfNeeded()
It should work. If not then you made mistake somewhere else.
That's not how updateConstraints() supposed to work.
You can, of course, modify any constraints then update layout like this:
self.topErrorViewConstarints.constant = 108
self.view.layoutIfNeeded()
But if you want to implement updateConstraints() you need the following:
// Call when you need to update your custom View
self.view.setNeedsUpdateConstraints()
Then inside the View override the function which will be called automatically by the system:
override func updateConstraints() {
super.updateConstraints()
topErrorViewConstarints.constant = currentTopErrorConstraint()
}
So, updateConstraints() is better suited for custom views with inner layout, that you don't want to be modified from outside. Also, it's good when you need to update many constraints at once.
Problem:
I have faced a similar problem in UiKit code. I created a view programmatically and the add constraint like below:
myView?.heightAnchor.constraint(equalToConstant: oldValue).isActive = true
It worked fine for the first time when the view is created. But need to update the height for some events, so I did like this:
myView?.heightAnchor.constraint(equalToConstant: oldValue).isActive = false
myView?.heightAnchor.constraint(equalToConstant: newValue).isActive = true
But the newValue had no effect, I also try with:
parentOfMyView.layoutIfNeeded()
It was also a failed attempt.
Solution:
I created a global variable to store the constraint:
private var viewHeightAnchor: NSLayoutConstraint?
Then use that when needed:
viewHeightAnchor?.isActive = false
viewHeightAnchor = myView?.heightAnchor.constraint(equalToConstant: 80.0)
viewHeightAnchor?.isActive = true
In this way I also didn't have to call layoutIfNeeded()
I'm using PNChart in a UIView and overtime my method runs it adds an addition line to the graph rather than recreating the entire graph. How do I clear a UIView, before add[ing]Subview?
#IBOutlet weak var lineChart: UIView!
...
// in function
theLineChart.chartData = [actualData]
theLineChart.strokeChart()
// want to clear self.lineChart here
self.lineChart.addSubview(theLineChart)
If by “clear” you mean remove all previously added subviews from it, you could try something like:
while let subview = lineChart.subviews.last {
subview.removeFromSuperview()
}
The caveat is that lineChart should remain a plain UIView (or your self-made subclass that you know the implementation of), as otherwise it may have internal subviews that you shouldn't remove.
Then again, if it is nothing but a plain UIView, you could simply replace the whole view with a new one (this might even make it simple to cross-fade between old and new views if such is desired).
I'm making a multiple choice quiz game, and my goal right now is to have four buttons that refresh by spinning around with new answer choices. I think that means I need a subview that animates and re-populates with new buttons--if that's incorrect or not best, please stop me here.
At any rate, I created the subview in my storyboard, put the buttons inside it (background is blue just to see it now):
I dragged that over to my ViewController to make an IBOutlet (buttonContainer) and added this code to my ViewDidLoad:
view.addSubview(buttonContainer)
let buttonTap = UITapGestureRecognizer(target:self, action: Selector("checkAnswer"))
buttonTap.numberOfTapsRequired = 1
buttonContainer.addGestureRecognizer(buttonTap)
buttonContainer.userInteractionEnabled = true
However: When I run it in the simulator, the blue background does not appear at all, but the buttons are still disabled.
Before creating the subview, both the buttons and the function (checkAnswer) they called all worked perfectly.
You don't need any of this code if you are creating everything in storyboard. Just create a new class for the containerview and connect the buttons as an outlet collection.
For example, your button container class might look something like this:
class ButtonContainerView: UIView {
#IBOutlet var answerButtons: [UIButton]!
func rotateButtons() {
for button in answerButtons {
var context = UIGraphicsGetCurrentContext()
UIView.beginAnimations(nil, context: &context)
UIView.setAnimationCurve(UIViewAnimationCurve.Linear)
UIView.setAnimationDuration(5.0)
button.transform = CGAffineTransformRotate(button.transform, CGFloat(M_PI))
UIView.commitAnimations()
}
}
}
Hello
Now I've been trying to display html links in a UITableView.
I've been trying to do this via adding instances of UITableViewCell to the tableview's subview.
func updateViewController(){
for name : String in currentDirectoryNames {
var pusher = UITableViewCell();
pusher.textLabel?.text = name;
listView.addSubview(pusher);
}
}
Sadly the result ends up with the text overlapped in one row :(
It looks like this...
Any ideas?
That's not how UITableView works, at all. I highly suggest you read Apple's documentation on TableView Programming. At the very least, you'll need to set your view controller as the dataSource for the table view and implement the -tableView:numberOfRowsInSection: and -tableView:cellForRowAtIndexPath: methods.
The func you're calling is adding all of your subviews with origin.y equal to 0 (which makes them look like they're all on top of each other). You may have to do something like set pusher.frame.origin.y to dynamically calculate based on which cell is being added.