Snapkit seems to be blocking all the GestureRecognizers on subviews - ios

I've created a custom UIView which has a UITapGestureRecognizer added to it in it's own init:
class BoxView: UIView, UIGestureRecognizerDelegate {
override init(frame: CGRect) {
super.init(frame: frame)
self.construct()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.construct()
}
convenience init() {
self.init(frame: CGRect.zero)
}
func construct() {
self.backgroundColor = UIColor.whiteColor()
self.frame.size = CGSize(width: 125, height: 125)
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
tapGesture.delegate = self
self.addGestureRecognizer(tapGesture)
}
func handleTap(sender: UITapGestureRecognizer? = nil) {
print("handleTap called!")
}
}
I'm using it in a ViewController, like this:
import Foundation
import UIKit
import AVFoundation
class StartViewController: UIViewController {
private var boxView: BoxView = BoxView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
self.view.addSubview(self.boxView)
/* block: XXX
self.boxView.snp_makeConstraints { (make) -> Void in
make.center.equalTo(self.view)
}
*/
}
}
The gestures don't seem to work as soon as I uncomment the block marked "XXX" in the ViewController which adds the SnapKit constraints. Can someone help me understand what's going on here? Am I using the gesture recognizer correctly?

Looks like I missed an important constraint, adding a "size" constraint on the custom view (BoxView) fixed it.
func construct() {
self.backgroundColor = UIColor.whiteColor()
self.frame.size = CGSize(width: 125, height: 125)
self.snp_makeConstraints { (make) -> Void in
make.size.equalTo(self.frame.size)
}
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
tapGesture.delegate = self
self.addGestureRecognizer(tapGesture)
}

Related

How to make a button work on a custom view from .xib instantiated programmatically

The button works if I add the view via the Interface Builder but it doesn't work when I add the view programmatically.
.xib design:
My custom view class:
class customView: UIView {
static let singleton1 = customView()
#IBOutlet weak var newView: UIView!
#IBAction func changeColor(_ sender: Any) {
newView.backgroundColor = UIColor.systemBlue // this is what the button should do
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
window?.windowLevel = UIWindow.Level(rawValue: CGFloat.greatestFiniteMagnitude)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
window?.windowLevel = UIWindow.Level(rawValue: CGFloat.greatestFiniteMagnitude)
}
func commonInit() {
let viewFromXib = Bundle.main.loadNibNamed("customView", owner: self, options: nil)![0] as! UIView
viewFromXib.bounds = self.bounds
addSubview(viewFromXib)
}
This is the way I instantiate it:
#IBAction func showView(_ sender: Any) {
let playerview = customView.singleton1
view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(playerview)
playerview.tag = 100
UIApplication.shared.keyWindow?.addSubview(playerview)// yes it needs to be in the window
Not sure if my initializer is wrong or something else.
You are trying to load "customView" xib into its own init method. Try below.
Replace commonInit() method with getView(frame:CGRect)->UIView
class customView: UIView {
static let singleton1 = customView()
#IBOutlet weak var newView: UIView!
#IBAction func changeColor(_ sender: Any) {
newView.backgroundColor = UIColor.systemBlue // this is what the button should do
}
override init(frame: CGRect) {
super.init(frame: frame)
window?.windowLevel = UIWindow.Level(rawValue: CGFloat.greatestFiniteMagnitude)
}
required init?(coder: NSCoder) {
super.init(coder: coder)
window?.windowLevel = UIWindow.Level(rawValue: CGFloat.greatestFiniteMagnitude)
}
func getView(frame:CGRect)->UIView{
let viewFromXib = Bundle.main.loadNibNamed("customView", owner: nil, options: nil)![0] as! UIView
viewFromXib.frame = frame
return viewFromXib
}
}
Replace showView() method
#IBAction func showView(_ sender: Any) {
let playerview = customView.singleton1.getView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 200))
self.view.addSubview(playerview)
playerview.tag = 100
UIApplication.shared.keyWindow?.addSubview(playerview)
}

how to prevent zooming of parantView while zooming of imageView

i have created CustomeView that contain Scroll-view.inside scroll view there is one container view that contain image view plus two button(Okay and cancel).
Following is my view-hierarchy.
CustomeView -> ScrollView -> ContainerView -> (imageView + OtherComponent).
There are two problem i faced.
while zoom in-out imageview,CustomeView is also zoomed in-out with
respect to Scrollview.
other component postion is changed while zoom in-out.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
backGroundView.backgroundColor = UIColor.white
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
backGroundView.addSubview(closeButton)
backGroundView.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(backGroundView.snp.centerX)
make.bottom.equalTo(backGroundView).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}
Following is code to add cameraPreview inside current ControllerView
let imagePreview = cameraPreview(image: image, frame: UIScreen.main.bounds,metaData:metaData)
self.view.addSubview(imagePreview)
your other component is zoom in-out because you put those component inside ScrollView.if you simply put those component out side of ScrollView than your component will not zoom in-out with respect to ScrollView.
Following is Source Code.
class cameraPreview : UIView , UIScrollViewDelegate {
var selectedImage : UIImage!
var backGroundView = UIView()
var imageScrollview = UIScrollView()
var metaData : [String:Any]?
let backgroundImageView = UIImageView()
var closeButton : UIButton = {
let button = UIButton(type: UIButtonType.custom)
button.setImage(UIImage(named:"closeWhite"), for: .normal)
button.addTarget(self, action: #selector(closeClick), for: .touchUpInside)
return button
}()
var okButton : UIButton = {
let button = UIButton()
button.setTitle("OK", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.cornerRadius = 15
button.addTarget(self, action: #selector(okClick), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
commoninit()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commoninit()
}
required init(image:UIImage,frame:CGRect,metaData:[String:Any]?) {
super.init(frame: frame)
self.selectedImage = image
self.metaData = metaData
commoninit()
}
override func layoutSubviews() {
}
func commoninit() {
self.backgroundColor = UIColor.white
backGroundView.backgroundColor = UIColor.clear
backgroundImageView.contentMode = .scaleAspectFit
self.addSubview(imageScrollview)
imageScrollview.addSubview(backGroundView)
backGroundView.addSubview(backgroundImageView)
self.addSubview(closeButton)
self.addSubview(okButton)
imageScrollview.snp.makeConstraints { (make) in
make.edges.equalTo(self)
}
backGroundView.snp.makeConstraints { (make) in
make.edges.equalTo(imageScrollview)
make.height.width.equalTo(self)
}
backgroundImageView.snp.makeConstraints { (make) in
make.edges.equalTo(backGroundView)
}
okButton.snp.makeConstraints { (make) in
make.width.equalTo(80)
make.height.equalTo(30)
make.centerX.equalTo(self)
make.bottom.equalTo(self).offset(-20)
}
closeButton.snp.makeConstraints { (make) in
make.width.height.equalTo(30)
make.left.equalTo(20)
make.top.equalTo(10)
}
backgroundImageView.image = selectedImage
imageScrollview.delegate = self
imageScrollview.minimumZoomScale = 1.0
imageScrollview.maximumZoomScale = 6.0
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return backgroundImageView
}
#objc func closeClick(sender:UIButton) {
self.removeFromSuperview()
}
#objc func okClick(sender:UIButton) {
if let topCotroller = UIApplication.shared.gettopMostViewController() {
self.removeFromSuperview()
let mediaDetailController = UploadDetailsViewController.instantiate(fromAppStoryboard: .Upload)
mediaDetailController.mediaImage = selectedImage
if metaData != nil {
mediaDetailController.exifDictionary = metaData![kCGImagePropertyExifDictionary as String] as? [String : AnyObject]
}
topCotroller.navigationController?.pushViewController(mediaDetailController, animated: true)
}
}
}

UISearchBar's text move to bottom when input content?

I customized a UISearchbar, the code is like this:
class CustomSearchBar: UIView {
lazy var searchBar: MySearchBar = {
let search = MySearchBar()
search.tintColor = UIColor.orange
search.barTintColor = UIColor.yellow
search.searchBarStyle = .minimal
search.searchTextPositionAdjustment = UIOffset(horizontal: 10, vertical: 0)
return search
}()
lazy var cancelButton: UIButton = {
let button = UIButton(type: .custom)
button.setTitle("取消", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
addSubview(searchBar)
addSubview(cancelButton)
}
private func setupConstraints() {
searchBar.snp.makeConstraints { (make) in
make.left.equalToSuperview().offset(20)
make.top.equalToSuperview().offset(6)
make.height.equalTo(32)
make.width.equalToSuperview().offset(-80)
}
cancelButton.snp.makeConstraints { (make) in
make.left.equalTo(searchBar.snp.right).offset(13)
make.centerY.equalTo(searchBar)
}
}
}
class MySearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func indexOfSearchFieldInSubviews() -> Int! {
var index: Int!
let searchBarView = subviews[0]
for (i, view) in searchBarView.subviews.enumerated() {
if view.isKind(of: UITextField.self) {
index = i
break
}
}
return index
}
override func draw(_ rect: CGRect) {
if let index = indexOfSearchFieldInSubviews() {
let searchField: UITextField = subviews[0].subviews[index] as! UITextField
searchField.leftView = UIImageView(image: UIImage(named: "Search_icon"))
searchField.font = UIFont.systemFont(ofSize: 14)
searchField.borderStyle = .none
searchField.layer.masksToBounds = true
searchField.layer.cornerRadius = 4
searchField.backgroundColor = barTintColor
searchField.contentVerticalAlignment = .center
searchField.placeholder = "input something"
}
}
}
we can see that it is very simple. Just added a searchBar and cancelButton.
Then I added it to navigationBar as subView, make it becomeFirstResponder in viewController.
class ViewController: UIViewController {
lazy var customeSearchBar = CustomSearchBar()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.addSubview(customeSearchBar)
customeSearchBar.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.centerY.equalToSuperview()
make.height.equalTo(44)
}
customeSearchBar.searchBar.becomeFirstResponder()
}
}
At first, I input content is ok.
When I continue input content, then the issue shows up:
It looks like the content move to the bottom. I tried to find something to fix the problem. But I didn't get it.
It is a strange thing that works fine in simulate and some iPhone. But one of my phone works wrong.

add tapGestureRecognizer to UIImageView in UIView and call method by protocol

I want to make touch recognized on an UIImageView(orange) in an UIView(blue). And hopefully, handle the touch event in a delegate method, which will be used in other classes. However, I can't make touch recognized with below code. How can I implement to recognize touch only in the ImageView(orange).
code:
import UIKit
protocol CustomViewDelegate {
func imageViewTapped()
}
class CustomView: UIView {
var delegate: CustomViewDelegate?
var imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.blue
self.imageView.backgroundColor = UIColor.orange
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapped))
imageView.addGestureRecognizer(tapGestureRecognizer)
self.addSubview(imageView)
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func tapped() {
print("tapped method called")
delegate?.imageViewTapped()
}
}
class ViewController: UIViewController, CustomViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let view = CustomView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
view.delegate = self
self.view.addSubview(view)
}
func imageViewTapped() {
print("come to view controller")
}
}
UIImageView default userinteraction is false, so enable imageView userinteraction in CustomView init.
imageView.isUserInteractionEnabled = true
Add Different UITapGestureRecognizer to different views and handle as per your requirement . or add tag and access like this
func tapOneAct(sender: UITapGestureRecognizer){
if let tag = sender.view?.tag {
switch tag {
case 1:
println("First View Tapped")
case 2:
println("Second View Tapped")
default:
println("Nothing Tapped")
}
}
}
enable isUserInteractionEnabled property on UIImageView as it defaults to false
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(CustomView.tapped(_:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
self.addSubview(imageView)
in tapGesture method
func tapgesture(_ sender: UITapGestureRecognizer){
if sender.state == .ended {
let touchLocation: CGPoint = sender.location(in: sender.view?.superview)
if imageView.frame.contains(touchLocation) {
print("tapped method called")
delegate?.imageViewTapped()
}
}
}

Setting rightview on subclass of UITextField after initialization

I try to set the rightView after UITextField initialized, but it gets nil. Here's my class:
class MyTextField: UITextField, UIGestureRecognizerDelegate {
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
prepare()
}
public override init(frame: CGRect) {
super.init(frame: frame)
prepare()
}
public convenience init() {
self.init(frame: .zero)
}
open private(set) var errorUIImageView = UIImageView(image: (resourceName: "ic_error_red"))
open func prepare() {
print("prepare")
let tap = UITapGestureRecognizer(target: self, action: Selector(("handleTap:")))
tap.numberOfTapsRequired = 1
tap.delegate = self
errorUIImageView.isUserInteractionEnabled = true
errorUIImageView.addGestureRecognizer(tap)
self.rightView = errorUIImageView // setting the rightView here
}
#IBInspectable
open var detail: String? {
get {
return detailLabel.text
}
set(value) {
detailLabel.text = value
if let v: String = value {
self.rightViewMode = .always
if self.rightView == nil {
print("nil") // this line is reached!
}
}
else {
self.rightViewMode = .never;
}
}
}
#IBInspectable
open var detailColor = UIColor.red {
didSet {
if let v: String = detailLabel.text {
detailLabel.attributedText = NSAttributedString(string: v, attributes: [NSForegroundColorAttributeName: detailColor])
}
}
}
#IBInspectable
open private(set) lazy var detailLabel = UILabel(frame: .zero)
func handleTap(sender: UITapGestureRecognizer) {
print("tapped")
}
}
Most of the code is taken from Material
If you edited your implementation after displaying it for the first time in interface builder, you might have to refresh all views.

Resources