I have a view that contains a stackView. The view is triggered on long gesture recognizer. I then use hittest to animate the contents of the stackView.
My view with the stackview is defined as follows:
lazy var starContainer : UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 182, height: 36))
view.layer.cornerRadius = 8
view.backgroundColor = .white
view.addSubview(starStack)
starStack.translatesAutoresizingMaskIntoConstraints = false
starStack.topAnchor.constraint(equalTo: view.topAnchor, constant: 1).isActive = true
starStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 1).isActive = true
starStack.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -1).isActive = true
starStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -1).isActive = true
view.layer.cornerRadius = view.frame.height / 2
// Set padding
let padding : CGFloat = 2
starStack.spacing = padding
starStack.layoutMargins = UIEdgeInsets(top: padding, left: padding, bottom: padding, right: padding)
starStack.isLayoutMarginsRelativeArrangement = true
// Generate shadow
view.layer.shadowColor = UIColor(white: 0.4, alpha: 0.4).cgColor
view.layer.shadowRadius = 8
view.layer.shadowOpacity = 0.5
view.layer.shadowOffset = CGSize(width: 0, height: 4)
for index in 0...4 {
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
if index == 0 {
imageView.tag = 5009
}
imageView.tag = index
imageView.image = UIImage(named: "icon_starred")
imageView.tintColor = UIColor(hex: Constants.Colors.secondary)
starStack.addArrangedSubview(imageView)
arrayStar.append(imageView)
}
return view
}()
I have then added a long gesture recognizer and whenever it detects a change then I move the views within the stackview individually:
#objc func showStars(gesture : UILongPressGestureRecognizer) {
if gesture.state == .changed {
let pressedLocation = gesture.location(in: self.starContainer)
let fixedYLocation = CGPoint(x: pressedLocation.x, y: starContainer.frame.height / 2)
let hitTestView = starContainer.hitTest(fixedYLocation, with: nil)
if hitTestView is UIImageView {
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
let stackView = self.starContainer.subviews.first
stackView?.subviews.forEach({ (imageView) in
imageView.transform = .identity
})
hitTestView?.transform = CGAffineTransform(translationX: 0, y: -50)
})
}
}
My question is how do I get the imageView tag that's been populated in the stackview?
Note: This animation is similar to the emoticons for a Facebook post.
You're thinking about this the wrong way. Your showStars method uses the call starContainer.hitTest() to figure out which view the user long-pressed. At that point, you know which view you are about to animate. Just remember that view.
Related
I am trying to change adjust the size of the a UIImageView that is the leftView of my UITextField without any success. The image is always huge. I would like it to have the same height as my UITextField. Here is what i have tried:
Result:
let topContainer: UIView = {
let view = UIView()
view.layer.borderWidth = 1.0
view.layer.borderColor = UIColor.appGrayExtraLightGray.cgColor
view.layer.cornerRadius = 5
view.clipsToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let emailField: UITextField = {
let view = UITextField()
view.backgroundColor = UIColor.orange
view.text = "myEmailAddress#gmail.com"
view.font = UIFont.systemFont(ofSize: 14)
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let s = view.safeAreaLayoutGuide
topContainer.addSubview(emailField)
emailField.topAnchor.constraint(equalTo: topContainer.topAnchor, constant: spacing).isActive = true
emailField.leadingAnchor.constraint(equalTo: topContainer.leadingAnchor, constant: spacing).isActive = true
emailField.trailingAnchor.constraint(equalTo: topContainer.trailingAnchor, constant: -spacing).isActive = true
emailField.bottomAnchor.constraint(equalTo: topContainer.bottomAnchor, constant: -spacing).isActive = true
/**Mail and clear button on eitherisde of textField*/
let mailView = UIImageView.init(frame: CGRect.init(x: 0, y: 0, width: 15, height: 15)) // Chaning the GREct has no effect on the size!!
mailView.image = UIImage.init(named: "mailGrayFilled")
emailField.leftView = mailView
emailField.leftViewMode = .always
emailField.clearButtonMode = .always
view.addSubview(topContainer)
topContainer.topAnchor.constraint(equalTo: s.topAnchor, constant: spacing).isActive = true
topContainer.leadingAnchor.constraint(equalTo: s.leadingAnchor, constant: spacing).isActive = true
topContainer.trailingAnchor.constraint(equalTo: s.trailingAnchor, constant: -spacing).isActive = true
Putting the UIImageView into a Container UIView seems to fix the issue when I test your code. This may also allow you to adjust the padding to suit your needs.
let iconContainer = UIView(frame: CGRect(x: 0, y: 0, width: 25, height: 15))
let mailView = UIImageView(frame: CGRect(x: 0, y: 0, width: 15, height: 15))
mailView.image = UIImage(named: "mailGrayFilled")
mailView.contentMode = .scaleAspectFit
iconContainer.addSubview(mailView)
emailField.leftViewMode = .always
emailField.leftView = iconContainer
emailField.clearButtonMode = .always
Using Swift5.1.3, XCode11.3, iOS13.3,
I try to reposition a custom navigationBar titleView.
Creating the custom view and adding it to my navigationBar works fine. (see code below)
Here an example: Please only consider the DarkGray NavigationBar on top with a Name-Label and a yellow round Image. The label and image shall be moved in y-direction!
The example on the left, I have successfully running. The example on the right I try to achieve. But without luck so far.
There is one missing thing I am struggling with since 4 hours.
How do I adjust the y-position (or .topAnchor constant offset) of a custom navigationBar titleView ???
The crash-message says:
'Unable to activate constraint with anchors
<NSLayoutYAxisAnchor:0x6000033ac900 "UIStackView:0x7fdcced39ea0.top"> and
<NSLayoutYAxisAnchor:0x6000033644c0 "UIView:0x7fdcd412ba20.top"> because they
have no common ancestor. Does the constraint or its anchors reference items
in different view hierarchies? That's illegal.'
Here is my code (please note the comment with the many exclamation marks - that is the y-offset trial and crash position of my code):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ...
// set up navigationItem and navigationController look and feeel
navigationController?.set_iOS12_lookAndFeel()
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationItem.largeTitleDisplayMode = .always
// create NavigationBar.titleView StackView (consisting of a label and a button)
let titleStackView = UIStackView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
titleStackView.isUserInteractionEnabled = true
titleStackView.axis = .horizontal
titleStackView.alignment = .center
titleStackView.spacing = 10.0
// stackView label
let labelWidth: CGFloat = UIScreen.main.bounds.width - 16.0 - 10.0 - 36.0 - 16.0 // FullScreenWidth minus (Leading + Spacing + ButtonWidth + Trailing)
let label = UILabel()
label.font = AppConstants.Font.NavBar_TitleFont
label.text = self.profileName
label.textColor = .white
label.tintColor = .white
// position label
label.translatesAutoresizingMaskIntoConstraints = false
label.widthAnchor.constraint(equalToConstant: labelWidth).isActive = true
// stackView button
let buttonWidth: CGFloat = 36.0
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: buttonWidth, height: buttonWidth)))
button.setImage(self.profileImageView.image, for: .normal)
button.isUserInteractionEnabled = true
button.addTarget(self, action: #selector(self.callProfileBtnMethod), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 0, width: 36, height: 36)
button.layer.cornerRadius = button.frame.size.width / 2
button.layer.masksToBounds = false
button.clipsToBounds = true
// position button
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
button.heightAnchor.constraint(equalToConstant: buttonWidth).isActive = true
// add label and button to stackView
titleStackView.addArrangedSubview(label)
titleStackView.addArrangedSubview(button)
// position titleStackView
titleStackView.translatesAutoresizingMaskIntoConstraints = false
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Here the code crashes !!!!!!!
titleStackView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100.0).isActive = true
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// position cockpitHeaderView (equal in size and position to titleStackView)
let cockpitHeaderView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: self.view.bounds.width, height: 88.0)))
cockpitHeaderView.backgroundColor = .green
cockpitHeaderView.isUserInteractionEnabled = true
cockpitHeaderView.addSubview(titleStackView)
cockpitHeaderView.leadingAnchor.constraint(equalTo: titleStackView.leadingAnchor, constant: 0.0).isActive = true
cockpitHeaderView.topAnchor.constraint(equalTo: titleStackView.topAnchor, constant: 0.0).isActive = true
cockpitHeaderView.trailingAnchor.constraint(equalTo: titleStackView.trailingAnchor, constant: 0.0).isActive = true
cockpitHeaderView.bottomAnchor.constraint(equalTo: titleStackView.bottomAnchor, constant: 0.0).isActive = true
// finally replace NavBar title by custom cockpitHeaderView
self.title = ""
self.navigationItem.titleView = cockpitHeaderView
}
How can I move the titleView correctly ???
I have a UIView called containerView.
Upon creating the UIView & Adding it to the main view, I'm trying to add a little shadow. My code:
let containerView = UIView()
let bg_clear = UIColor(hexString: "#34495E")
containerView.backgroundColor = bg_clear
containerView.layer.cornerRadius = 15
containerView.clipsToBounds = false
containerView.dropShadow() // Generate Shadow
view.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.heightAnchor.constraint(equalToConstant: 50).isActive = true
containerView.widthAnchor.constraint(equalToConstant: 300).isActive = true
containerView.centerXAnchor.constraint(equalTo: tabBar.centerXAnchor).isActive = true
Code to generate the Shadow:
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor(hexString: "#000000").cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -1, height: 1)
layer.shadowRadius = 1
layer.shadowPath = UIBezierPath(rect: bounds).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
This should help :
extension UIView {
func addShadow(withOpacity opacity:Float, radius:CGFloat, andColor color:UIColor) {
self.layer.shadowColor = color.cgColor
self.layer.shadowOffset = CGSize(width:-1.0, height:1.0)
self.layer.shadowOpacity = opacity
self.layer.shadowRadius = radius
}
}
Example :
containerView.addShadow(withOpacity: 0.4, radius: 0.4, andColor: .black)
A few points to note:
Make sure your container view is not too near the left or the bottom of the screen, because the shadow is going show up to the left and below the container view.
Make sure you use a rounded rect for the shadow path, because your container view has a non-zero corner radius.
If you still can't see the shadow, try increasing the shadow offset.
You can copy and paste this code into a playground and open the live view:
extension UIView {
func dropShadow(scale: Bool = true) {
layer.masksToBounds = false
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.5
layer.shadowOffset = CGSize(width: -5, height: 5) // I made this larger
layer.shadowRadius = 1
// I used a rounded rect here
layer.shadowPath = UIBezierPath(roundedRect: self.bounds, cornerRadius: self.layer.cornerRadius).cgPath
layer.shouldRasterize = true
layer.rasterizationScale = scale ? UIScreen.main.scale : 1
}
}
let view = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
view.backgroundColor = .white
let containerView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
let bg_clear = UIColor.green
containerView.backgroundColor = bg_clear
containerView.layer.cornerRadius = 15
containerView.clipsToBounds = false
containerView.dropShadow() // Generate Shadow
view.addSubview(containerView)
PlaygroundPage.current.liveView = view
You should see:
I have one method that shows a photo and zooms in it if the user wants. I used a scroll view so that the user could scroll if he zooms in a photo. My zoom works but it zoom into the top left corner and my scroll view doesn't scroll anywhere else even though it shows vertical and horizontal bars responsible for scroll moving.
class ImageViewController: UIViewController, UIScrollViewDelegate {
var myscrollView: UIScrollView!
var imgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
myscrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))
self.view.addSubview(myscrollView)
myscrollView.delegate = self
// myscrollView.scrollRectToVisible(CGRect(x: 100, y: 100, width: 1, height: 1), animated: true)
// myscrollView.setContentOffset(CGPoint(x: 0, y: view.frame.size.height), animated: true)
// myscrollView.contentSize = CGSize(width: view.frame.width, height: view.frame.height)
print("\(myscrollView.frame.height) \(myscrollView.frame.width)")
//myscrollView.contentSize = CGSize(width: 1000, height: 2000)
myscrollView.isScrollEnabled = true
myscrollView.minimumZoomScale = 1.0
myscrollView.maximumZoomScale = 10.0//maximum zoom scale you want
myscrollView.zoomScale = 1.0
myscrollView.alwaysBounceVertical = false
myscrollView.alwaysBounceHorizontal = false
myscrollView.showsVerticalScrollIndicator = true
myscrollView.flashScrollIndicators()
// scrollView.isPagingEnabled = false
// self.scrollView.contentSize = self.view.frame.size * 2//or what ever size you want to set
// myscrollView.contentOffset = CGPointMake((myscrollView.contentSize.width-myscrollView.frame.size.width)*.5f, .0f);//scroll to center
// myscrollView.contentOffset = CGPoint((myscrollView.contentSize.width-myscrollView.frame.size.width)*.5f, .0f)
myscrollView.addSubview(imgView)
//self.view.addSubview(imgView)
imgView!.layer.cornerRadius = 11.0
imgView.bounds = myscrollView.bounds
//imgView!.clipsToBounds = false
imgView.isUserInteractionEnabled = true
imgView.heightAnchor.constraint(equalToConstant: 50).isActive = true
// imgView.widthAnchor.constraint(equalToConstant: 50).isActive = true
imgView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 64).isActive = true
imgView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
imgView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
// imgView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
imgView.translatesAutoresizingMaskIntoConstraints = false
myscrollView.contentSize = CGSize(width: imgView.frame.width , height: 2000)
// myscrollView.contentSize = imgView.bounds.size
// Do any additional setup after loading the view.
}
// func scrollViewDidZoom(_ scrollView: UIScrollView) {
// var offsetY: CGFloat!
// offsetY = 0;
// if (scrollView.zoomScale > 1){
// offsetY = CGFloat(scrollView.contentOffset.y);
// }
// //CGPoint(x: scrollView.contentOffset.x, y: offsetY)
// scrollView.setContentOffset(CGPoint(x:
// scrollView.contentOffset.x, y: offsetY), animated: true)
//
// // [aScrollView setContentOffset:
// CGPointMake(aScrollView.contentOffset.x, offsetY)];
//
// }
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
// override func viewDidLayoutSubviews() {
// myscrollView.delegate = self
// myscrollView.contentSize =
//CGSize(width:self.view.frame.size.width, height: 1000)
// }
// func viewForZooming(in scrollView: UIScrollView) -> UIView? {
// return imgView
// }
}
I tried to set contentSize manually to some big or small values and well as using frame.width and height but none of them seem to be working. Can someone please help what is my problem? Thanks in advance :)
Just try these lines of code and check, What are you missing-
let screenSize: CGRect = UIScreen.main.bounds
let scrollView = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize = CGSize(width: screenSize.width, height: 1300)
scrollView.frame = CGRect(x: 0, y: 70, width: screenSize.width, height: screenSize.height-70)
scrollView.backgroundColor = UIColor.clear
view.addSubview(scrollView)
}
Try implementing scrollViewDidZoom method as follows;
func scrollViewDidZoom(_ scrollView: UIScrollView) {
let offsetX = max((scrollView.bounds.size.width-scrollView.contentSize.width) * 0.5, 0.0)
let offsetY = max((scrollView.bounds.size.height-scrollView.contentSize.height) * 0.5, 0.0)
self.scrollView.contentInset = UIEdgeInsetsMake(offsetY, offsetX, 0, 0)
}
I am creating an animation which brings an image and a label from the left to the centre of the view.
imageLogo.isHidden = true is not hidden when App is run
labelLogo is not shown at all on the view
I have been reading tutorials, but I just don't see what is wrong with my code.
let paymentLogo = UIImage(named: "paymentImage")
var imageLogo:UIImageView!
var overlayView = UIView()
var logoAppeared:Bool!
let labelLogo = UILabel()
override func viewDidLayoutSubviews() {
//move picture off the screen here
self.imageLogo = UIImageView(image:paymentLogo)
imageLogo.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
imageLogo.center.x -= 400
self.view.addSubview(imageLogo)
self.labelLogo.frame =
CGRect(x: 0, y: 0, width: 200, height: 21)
self.labelLogo.center.x -= 400
self.labelLogo.text = "Booking Completed"
self.labelLogo.textAlignment = .center
self.view.addSubview(labelLogo)
}
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 2.0, delay: 0.1, options: [], animations: {
//animate paymentCompletedLogo
self.overlayView = UIView(frame: self.view.frame)
self.overlayView.backgroundColor = UIColor.black
self.overlayView.alpha = 0.4
//animate labelLogo
self.labelLogo.frame = CGRect(x: self.view.center.x, y: 90, width: 200, height: 21)
self.labelLogo.backgroundColor = UIColor.gray
self.labelLogo.text = "Booking Completed"
self.labelLogo.textColor = .black
self.labelLogo.textAlignment = .center
//animate imageLogo
self.imageLogo.frame =
CGRect(x: self.view.center.x,y: self.view.center.y,width: 100,height: 100)
self.view.addSubview(self.overlayView)
self.view.addSubview(self.imageLogo)
self.view.addSubview(self.labelLogo)
}) { finished in
self.overlayView.isHidden = true
self.imageLogo.isHidden = true //it is not hidden in simulator
self.logoAppeared = true
}
}
I tried removing following your code from animation method it worked fine.
You are trying to add subview in animation method. imageLogo and labelLogo is all ready added in view.
self.view.addSubview(self.overlayView)
self.view.addSubview(self.imageLogo)
self.view.addSubview(self.labelLogo)