Removing one subview from superview in swift somehow makes another view invisible - swift

Can someone help me understand why removing a subview from superview somehow makes another subview in the superview disappear visually? Code is shown below:
private func setupSubviews() {
...
...
self.view.addSubview(self.feedView) feedView
self.view.addSubview(self.bannerView)
}
private func setupConstraints() {
self.view.backgroundColor = .white
self.feedView.backgroundColor = .white
self.feedView.topAnchor.constraint(equalTo:
self.view.safeAreaLayoutGuide.topAnchor).isActive = true
self.bannerView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
constrain(self.feedView, self.view, self.bannerView) { feedView, container, banner in
feedView.left == container.left
feedView.right == container.right
feedView.bottom == banner.top
banner.left == container.left
banner.right == container.right
}
for view in self.view.subviews {
print("View: ", view)
//self.feedView.removeFromSuperview()
if view is DFPBannerView {
print(self.bannerView.superview)
self.bannerView.removeFromSuperview()
}
}
for view in self.view.subviews {
print("&&&***", view) //show the feedViews is still here
print(view.isHidden) //shows false
}
In the setupSubviews, I've added to subviews, feeView and bannerView. I need to remove the bannerView from the superview but for whatever reason when self.bannerView.removeFromSuperview() runs, the feedView also disappear visually. It's actually still in the self.views.subviews as I tried to see if it's still there but for some reason, nothing shows up other than just the white background.
If however I remove the feedView instead, the bannerView still shows up visually.
I naively thought perhaps somehow the feedView's isHidden property got set to true so I tried self.feedView.isHidden = false, but that didn't do anything.
So my question is what am I missing?

Related

Prevent cell content from "jumping" when applying constraint

I have a subclassed UICollectionViewCell and I want it to expand when tapped.
To achieve this, I put the title into a view ("titleStack") and the body into a separate view ("bodyStack"), and then put both of them into a container UIStackView ("mainStack"). I then constrain the contentView of the cell to the leading, trailing, and top edges of mainStack.
When the cell is selected, a constraint is applied that sets the bottom of the contentView's constraint to be the bottom of bodyStack. When it's unselected, I remove that constraint and instead apply one that sets the contentView's bottom constraint equal to titleStack's bottom constraint.
For the most part this works well, but when deselecting, there's this little jump, as you can see in this video:
What I would like is for titleStack to stay pinned to the top while the cell animates the shrinking portion, but it appears to jump to the bottom, giving it a sort of glitchy look. I'm wondering how I can change this.
I've pasted the relevant code below:
private func setUp() {
backgroundColor = .systemGray6
clipsToBounds = true
layer.cornerRadius = cornerRadius
setUpMainStack()
setUpConstraints()
updateAppearance()
}
private func setUpMainStack() {
contentView.constrain(mainStack, using: .edges, padding: 5, except: [.bottom])
mainStack.add([titleStack, bodyStack])
bodyStack.add([countryLabel, foundedLabel, codeLabel, nationalLabel])
}
private func setUpConstraints() {
titleStack.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
closedConstraint =
titleStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
closedConstraint?.priority = .defaultLow // use low priority so stack stays pinned to top of cell
openConstraint =
bodyStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
openConstraint?.priority = .defaultLow
}
/// Updates the views to reflect changes in selection
private func updateAppearance() {
UIView.animate(withDuration: 0.3) {
self.closedConstraint?.isActive = !self.isSelected
self.openConstraint?.isActive = self.isSelected
}
}
Thanks so much!
I was able to solve this by simply showing and hiding my "bodyStack" as well as using "layoutIfNeeded." I removed closedConstraint and openConstraint and just gave it a normal bottom constraint.
The relevant code:
func updateAppearance() {
UIView.animate(withDuration: 0.3) {
self.bodyStack.isHidden = !self.isSelected
self.layoutIfNeeded()
}
}

Remove BackgroundView from UITargetedPreview in Swift

I'm trying to remove the background view for my UITargetPreview. I made the background color clear, however, you can still see the frame of the background.
This is what it currently looks like:
I currently have a view that has the text container and the image inside of it and that's what I use as the view for the UITargetedPreview.
Is there a way to only show the image and the text and not the background frame?
There is a tricky method to hide the shadow and to do that you should find a view with _UIPlatterSoftShadowView class name in the view hierarchy and then hide it.
func viewByClassName(view: UIView, className: String) -> UIView? {
let name = NSStringFromClass(type(of: view))
if name == className {
return view
}
else {
for subview in view.subviews {
if let view = viewByClassName(view: subview, className: className) {
return view
}
}
}
return nil
}
override func tableView(_ tableView: UITableView, willDisplayContextMenu configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) {
DispatchQueue.main.async {
if let window = UIApplication.shared.delegate?.window! {
if let view = self.viewByClassName(view: window, className: "_UIPlatterSoftShadowView") {
view.isHidden = true
}
}
}
}
NOTE: It's not documented internal class and can be changed anytime further but it works now on both ios 13/14.
Have you tried subclassing the UIView as a UIControl?
I had a similar issue but in my case the view for UITargetedPreview was glitchy. However, changing the UIView to a UIControl fixed everything.
try removing shadow of that background view.
You need to study UIBezierPath() to outline the specific area you want to enclose before you present the target view.
After that, you shall assign the specific path to shadow path / visible path
let params = UIPreviewParameters()
params.backgroundColor = .clear
if #available(iOS 14.0, *) {
params.shadowPath = bubblePath
} else {
params.visiblePath = bubblePath
}

Change size of view and move the surrounding views along

I have a view that I want to change the size of with animation. Below that view are other views that I want to move accordingly.
I have created a new project just so simplify this for me to get this function to work properly. In this project I only have viewOne, viewTwo and a button to control this. There is also a boolean called "blue".
#IBAction func bttn() {
if blue {
blue = false
UIView.animate(withDuration: 1) {
self.viewOne.frame.size.height = 64
}
}else {
blue = true
self.viewOne.frame.size.height = 100
}
}
When I tap the button I expect viewOne to increase in size and viewTwo to be moved down since there is a constraint between the top and bottom of the views.
But the actual result is that viewOne increase but viewTwo does not move.
Only autolayout can do this as changing frames doesn't apply the constraints , You need to create a height outlet for view1 then
self.view1Height.constant = 64
UIView.animate(withDuration: 1) {
self.view.layoutIfNeeded()
}

Swift: addGestureRecognizer not work for stackview children

Codes:
for ... {
let view = CategoryClass.createMyClassView()
view.myLabel.text = packTitle
view.twoLabel.text = packText
view.bgCaategory.layer.cornerRadius = 30
i = i + 1
if(i == 1){
selectPackId = packId!;
view.imgSelect.image = UIImage(named: "selected")
} else {
view.imgSelect.image = UIImage(named: "select")
}
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSendData(sender:))))
self.stackView.addArrangedSubview(view)
}
#objc func handleSendData(sender: UITapGestureRecognizer) {
print("H 1")
}
If i click on view, nothing print "H 1"
I want if i click on view, get id or another value of view
If adding isUserInteractionEnabled as suggested by Marcel still doesn't work, also make sure that every parent view in the hierarchy has a valid frame (you can check it in Debug View Hierarchy).
E.g. it happened to me to add a UIStackView into a parent UIView but the layout constraints were not correct, so I ended up having the parent UIView frame size as 0 (but the UIStackView was still visible).
If you create the UIStackView in interface builder, the isUserInteractionEnabled property is false by default. This means that the view and all it's child views won't respond to user interaction.
When you create a view in code, this property is true be default.
Add:
stackView.isUserInteractionEnabled = true
You only have to add this once, in your viewDidLoad for example.
The reason it doesn’t work is possibly a wrong method signature. The correct signature for recognizer actions is this:
recognizerAction(_ recognizer: UIGestureRecognizer)

Dragging NSSplitView divider does not resize views

I'm working with Cocoa and I create my views in code (no IB) and I'm hitting an issue with NSSplitView.
I have a NSSplitView that I configure in the following way in my view controller, in Swift:
override func viewDidLoad() {
super.viewDidLoad()
let splitView = NSSplitView()
splitView.isVertical = true
splitView.addArrangedSubview(self.createLeftPanel())
splitView.addArrangedSubview(self.createRightPanel())
splitView.adjustSubviews()
self.view.addSubview(splitView)
...
}
The resulting view shows the two subviews and the divider for the NSSplitView, and one view is wider than the other. When I drag the diver to change the width, as soon as I release the mouse, the divider goes back to its original position, as if pulled back by a "spring".
I can't resize the two subviews; the right one always keeps a fixed size. However, nowhere in the code I fix the width of that subview, or any of its content, to a constant.
What I would like to achieve instead is that the right view size is not fixed, and that if I drag the divider at halfway through, the subviews will resize accordingly and end up with the same width.
This is a screen recording of the problem:
Edit: here is how I set the constraints. I'm using Carthography, because otherwise setting constraints in code is extremely verbose beyond the most simple cases.
private func createLeftPanel() -> NSView {
let view = NSView()
let table = self.createTable()
view.addSubview(table)
constrain(view, table) { view, table in // Cartography magic.
table.edges == view.edges // this just constraints table.trailing to
// view.trailing, table.top to view.top, etc.
}
return view
}
private func createRightPanel() -> NSView {
let view = NSView()
let label = NSTextField(labelWithString: "Name of item")
view.addSubview(label)
constrain(view, label) { view, label in
label.edges == view.edges
}
return view
}