I'm using Card.io to scan cards inside a custom UIView. The issue I have is that the camera view is taking up the view frame resulting in borders left and right. There's a frame property called cameraPreviewFrame that assigns the window and I think I need to override this property and return it's width and height.
Is this possible or is there something else I need to do?
My current code is:
var cardScanView: CardIOView = {
let csv = CardIOView()
csv.guideColor = .blue
csv.hideCardIOLogo = true
csv.allowFreelyRotatingCardGuide = false
csv.backgroundColor = .purple
return csv
}()
I'm adding this programatically inside a collection view cell and it does work in it's current form. Just the view is off.
Thanks
Related
I'd like to add a gradient to a collection view cell's background in the context of the new collection view with compositional layouts. Here's an example of how a cell's background is configured from Apple's sample code Implementing Modern Collection Views in line 180 of EmojiExplorerViewController:
func configuredGridCell() -> UICollectionView.CellRegistration<UICollectionViewCell, Emoji> {
return UICollectionView.CellRegistration<UICollectionViewCell, Emoji> { (cell, indexPath, emoji) in
var content = UIListContentConfiguration.cell()
content.text = emoji.text
content.textProperties.font = .boldSystemFont(ofSize: 38)
content.textProperties.alignment = .center
content.directionalLayoutMargins = .zero
cell.contentConfiguration = content
var background = UIBackgroundConfiguration.listPlainCell()
background.cornerRadius = 8
background.strokeColor = .systemGray3
background.strokeWidth = 1.0 / cell.traitCollection.displayScale
cell.backgroundConfiguration = background
}
}
Since the new UIBackgroundConfiguration is a structure rather than a layer-backed UIView subclass, I can't just add a CAGradientLayer instance as a sublayer.
What would be a good approach to adding a gradient to a cell background configuration?
Since the new UIBackgroundConfiguration is a structure rather than a layer-backed UIView subclass, I can't just add a CAGradientLayer instance as a sublayer.
Yes, you can. The fact that UIBackgroundConfiguration is a struct is irrelevant. It has a customView property that's a view, and that will be used as the background view (behind the content view) in the cell. So set that view to something (it is nil by default) and you're all set.
Here's an example. This is a toy table view for test purposes, but the test is exactly about configuration objects, so it is readily adaptable to demonstrate the technique. It doesn't matter whether you're using a table view, a collection view, or neither, as long as you are using something that has a UIBackgroundConfiguration property. As you can see, I've made a vertical gradient from black to red as the background to my cells.
Here's the relevant code. First, I have defined a gradient-carrier view type:
class MyGradientView : UIView {
override static var layerClass: AnyClass { CAGradientLayer.self }
}
Then, I use that view as the background view when I configure the cell:
var back = UIBackgroundConfiguration.listPlainCell()
let v = MyGradientView()
(v.layer as! CAGradientLayer).colors =
[UIColor.black.cgColor, UIColor.red.cgColor]
back.customView = v
cell.backgroundConfiguration = back
Whatever else you want to achieve is merely a variant of the above. For example, you could use an image view or a solid background view and combine them with the gradient view. The point is, the customView is the background view, and whatever view you set it to will be displayed in the background of the cell.
I should also point out that there is another way to do this, namely, to use a cell subclass and implement updateConfigurationUsingState:. The advantage of this approach is that once you've given the background configuration a customView, you can just modify that customView each time the method is called. You can use this technique to respond to selection, for example, as I have demonstrated in other answers here (such as https://stackoverflow.com/a/63064099/341994).
I have a custom UIView that's my little component lets call it PlaceholderView.
My UITableViewCell prototype has a Label and a PlaceholderView that sits inside a UIStackView that's vertically axis.
The PlaceholderView, is supposed to call some custom code that goes to a cache to retrieve a specific view then it adds it to the SubView of the PlaceholderView.
I want this subview to take up the whole surface of the entire PlaceholderView. How do I go about doing that? I tried this but not sure if it does the job
if (view != null)
{
AddSubview(view);
view.SizeToFit();
}
Second question. These view's that I am adding, when I create them during design time, I make a new storyboard, drag and drop a ViewController then proceed to place other controls like Labels and Button's on it.
How do I restrict this ViewController's overall height so it's completely fixed size? I know I can set the simulated metrics, and I am also setting the View. Frame's size to restrict the height.
Are there better ways to make these views constrained easier?
Currently, when I am setting these fixed height values, it does cause some weird issues with overlaps if I set UITableView.RowHeigh to AutomaticDimension.
// attaches all sides of the child to its parent view
extension UIView {
func layoutToSuperview() {
guard let view = self.superview else {return}
self.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
self.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
self.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
self.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
}
}
Usage:
Instead of view.SizeToFit() use view.layoutToSuperview()
I want this subview to take up the whole surface of the entire
PlaceholderView
You can try autolayout:
if (view != null)
{
view.TranslatesAutoresizingMaskIntoConstraints = false;
AddSubview(view);
view.LeadingAnchor.ConstraintEqualTo(this.LeadingAnchor).Active = true;
view.TopAnchor.ConstraintEqualTo(this.TopAnchor).Active = true;
view.TrailingAnchor.ConstraintEqualTo(this.TrailingAnchor).Active = true;
view.BottomAnchor.ConstraintEqualTo(this.BottomAnchor).Active = true;
}
Are there better ways to make these views constrained easier?
The answer is also auto layout. Set a control's height constraint to claim how much space it wants to take. See this thread for more details: Using Auto Layout in UITableView for dynamic cell layouts & variable row heights.
Though it is native oc, you can see its concept.
Whenever I use the pickerview to switch views from Auto Rent to Schedule Rent it works perfectly. It is when I switch from Schedule Rent to Auto Rent that this black bar appears. I have attached the hierarchy of my content view. I thought it had to do with previous constraints added, so I remove a StackView whenever one view is chosen. For example, if Auto Rent is chosen, then I remove the StackView where the Schedule View is in:
//Holds Temp Stackviews
var stackViewHolder1: UIView?
var stackViewHolder2: UIView?
override func viewDidLoad() {
super.viewDidLoad()
stackViewHolder1 = stackViewMain.arrangedSubviews[0]
stackViewHolder2 = stackViewMain.arrangedSubviews[1]
}
if txtRentType.text == "Auto Rent" {
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView!.isHidden = true
stackViewMain.removeArrangedSubview(tempView!)
if(tempView1!.isHidden == true){
tempView1!.isHidden = false
stackViewMain.addArrangedSubview(tempView1!)
}
else{
let tempView = stackViewHolder1
let tempView1 = stackViewHolder2
tempView1!.isHidden = true
stackViewMain.removeArrangedSubview(tempView1!)
if(tempView!.isHidden == true){
tempView!.isHidden = false
stackViewMain.addArrangedSubview(tempView!)
}
}
I have tried deleting one view and toggling only one view has being hidden and that removes the black bar issue. There is no constraint with the stackViews and Content View.
EDIT:
The screen highlighted is the scrollView. The one after is the contentView. UIWindow goes black in the back.
My Title Bar at the top ends up in the middle somehow.
You can try to modify your stack distribution property
stack.distribution = .equalCentering
After you won't need to use this:
.removeArrangedSubview()
.addArrangedSubview()
When you hide some view, the other view take all space of your stack, you don't need to update your constraints. You can try it on interface builder to see how it works.
are you pinning your scrollview and the content view to the bottom with constraints?
If the content view is a stack view you can pin it to the bottom as well with layout constraints and play with the content distribution.
You don't need to use remove/Add arranged subviews.
when hiding a view in a stackView its automatically removed.
so i think you can just hide or show the stackViewMain.subviews[0] o stackViewMain.subviews[1]
i'm with objc maybe i do a mistake but it would be something like this :
if txtRentType.text == "Auto Rent" {
stackViewMain.arrangedSubviews[0].isHidden = true;
stackViewMain.arrangedSubviews[1].isHidden = false;
}else{
stackViewMain.arrangedSubviews[1].isHidden = true;
stackViewMain.arrangedSubviews[0].isHidden = false;
}
I've created a subclass of UIControl called 'TestButton' with a label and an imageview subview. That object is created with a frame, and as part of the init process I create the subview elements.
These 'TestButtons' are created programmatically, I never use them in the StoryBoard.
Code snippet:
class TestButton: UIControl {
var iconImageView: UIImageView?
var labelView: UILabel?
required init(size: CGSize, icon: UIImage? text: String?) {
super.init(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height) )
if ( (icon != nil) && (text != nil) ) {
self.iconImageView = UIImageView()
self.iconImageView?.translatesAutoresizingMaskIntoConstraints = false
self.iconImageView?.contentMode = .center
self.iconImageView?.image = icon
self.iconImageView?.backgroundColor = UIColor.yellow // test: show bounds
self.labelView = UILabel()
self.labelView?.translatesAutoresizingMaskIntoConstraints = false
self.labelView?.text = "Test"
self.labelView?.backgroundColor = UIColor.blue
self.addSubview(self.iconImageView!)
//self.addSubview(self.labelView!)
// Setup constraints on created subview(s)
self.iconImageView?.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
self.iconImageView?.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
self.iconImageView?.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
self.iconImageView?.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
}
override func layoutSubviews() {
super.layoutSubviews()
print("iconframe: \(self.iconImageView!.frame)")
}
In the sample above, I've removed the label from the mix. I'm only trying to get the imageView constraints to work and effectively size the imageView to the view. This does not work, the image appears full size and the constraints appear to have been ignored. I've tried moving the constraints code into updateConstraints and calling that - all appears to work but again the constraints are not applied.
layoutSubviews does get called when you would expect it to be but the imageView frame is unmodified. There are no messages in the output window, it just silently doesn't work.
My question is; have I somehow disabled autoLayout by specifying the parent's frame? I would have expected autoLayout to still work within the bounds of the parent's frame?
Sorry if this has been answered once or many times before. I'm not actually sure what I'm searching for or the correct question to ask, only posted after a day of trawling SO. Thanks
The behaviour of TestButton view will depend on how it is constrained within its superview.
In NSLayoutConstraints both participating attributes (or anchors) are equal "partners": with just those four constraints you have, imageView will take full frame of it's parent (TestButton), but at the same time TestButton will be expanded to be big enough for a full-size image.
You can apply other constraints to TestButton view to prevent the latter.
To understand why standard views behave like that, look at intrinsicContentSize property (docs). It is implemented by standard controls, and tells the auto layout system how big the view should be, purely based on it's content (UIButton or UISwitch are auto-sized like that, for example). UIImageView's intrinsicContentSize is the size of its image, that's why it expands full-size if nothing is preventing it.
i'm trying to add a sublayer behind the imageView however the issue is that since it is using constraints it can't seem to figure out the position and just places sublayer in left corner? i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
viewDidLoad
//GroupProfile ImageView
imageGroupProfile = UIImageView(frame: CGRect.zero)
imageGroupProfile.backgroundColor = UIColor.white
imageGroupProfile.clipsToBounds = true
imageGroupProfile.layer.cornerRadius = 50
self.view.addSubview(imageGroupProfile)
imageGroupProfile.snp.makeConstraints { (make) -> Void in
make.height.equalTo(100)
make.width.equalTo(100)
make.centerX.equalTo(self.view.snp.centerX)
make.centerY.equalTo(self.view.snp.centerY).offset(-40)
}
let pulseEffect = LFTPulseAnimation(repeatCount: Float.infinity, radius:160, position:imageGroupProfile.center)
self.view.layer.insertSublayer(pulseEffect, below: imageGroupProfile.layer)
i've tried to add the LFTPulseAnimation to viewDidLayoutSubViews but then everytime i reopen the app it will add one on top.
Nevertheless that is the way to do it. Just add a Bool property so that your implementation of viewDidLayoutSubViews inserts the layer only once:
var didLayout = false
override func viewDidLayoutSubviews() {
if !didLayout {
didLayout = true
// lay out that layer here
}
}
The reason is that you don't have the needed dimensions until after viewDidLayoutSubviews tells you that (wait for it) your view has been laid out! But, as you rightly say, it can be called many times subsequently, so you also add the condition so that your code runs just once, namely the first time viewDidLayoutSubviews is called.