I am trying to zoom image view inside a UIView, but it zoom outside the UIView.
Condition : without using scrollview
here is my code :
extension UIImageView {
func enableZoom() {
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(startZooming(_:)))
isUserInteractionEnabled = true
addGestureRecognizer(pinchGesture)
}
#objc
private func startZooming(_ sender: UIPinchGestureRecognizer) {
let scaleResult = sender.view?.transform.scaledBy(x: sender.scale, y: sender.scale)
guard let scale = scaleResult, scale.a > 1, scale.d > 1 else { return }
sender.view?.transform = scale
sender.scale = 1
}
}
lazy var ImgView : UIImageView = {
let i = UIImageView()
i.enableZoom()
return i
}()
lazy var topView : UIView = {
let c = UIView()
return c
}()
in viewdidload
self.view.layout(self.ImgView).top(90).left().right().height(topViewHeight)
self.topView = self. ImgView
Related
i add about 20+ images to the stack and by tap on image i need to change borderColor, but with this code i have error " "-[__NSArray0 tapOnView]: unrecognized selector sent to instance "
Add stack here
private lazy var mainHStack: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .fill
stack.spacing = 8
return stack
}()
Add constraints here
scrollView.addSubview(mainHStack)
mainHStack.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
tapOnMediaView()
Configure stack here
func configureMediaViewer(withImages images: [String]) {
for image in images {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.layer.cornerRadius = 8
imageView.clipsToBounds = true
imageView.setImage(imageUrl: image)
mainHStack.addArrangedSubview(imageView)
imageView.snp.makeConstraints { make in
make.width.height.equalTo(60)
}
}
}
Check if i tapped on image here
func tapOnMediaView() {
let arrangedImages = mainHStack.arrangedSubviews
let tap = UITapGestureRecognizer(target: arrangedImages, action: #selector(tapOnView))
if let tag = tap.view?.tag {
let currentImage = mainHStack.arrangedSubviews[tag]
currentImage.layer.borderWidth = 1
currentImage.layer.borderColor = UIColor.orange.cgColor
}
addGestureRecognizer(tap)
}
TapOnView has a delegate
#objc func tapOnView() {
delegate?.tapOnView()
}
Delegate
protocol PhotosViewProtocol: AnyObject {
func tapOnView()
}
Extension for mainVC
extension MediaViewerViewController: PhotosViewProtocol {
func tapOnView() {
print("check if works")
//not works :C
}
}
how do i change my code to get a result?
I want to make a 3d game on Xcode, SceneKit. To test it out, I started a simple project about a box, that if you tapped, moves in X direction. My question is how do you make the tap gesture work in a 3d game? I've been trying to add it but it just doesn't work. Here is all the code that I've written in case I'm doing something wrong:
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named:"art.scnassets/Box.scn")
let newscene = self.view as! SCNView
newscene.scene = scene
let tap = UITapGestureRecognizer(target: self, action: #selector(taps(tap:)));newscene.addGestureRecognizer(tap)
tap.numberOfTapsRequired = 1
tap.numberOfTouchesRequired = 1
}
override var shouldAutorotate: Bool {
return true
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
#objc func taps(tap:UITapGestureRecognizer){
let newscene = self.view as! SCNView
let scene = SCNScene(named: "art.scnassets/Box.scn")
let box = scene?.rootNode.childNode(withName: "Box", recursively: true)
let place = tap.location(in: newscene)
let tapped = newscene.hitTest(place, options: [:])
if tapped.count > 0{
box?.runAction(SCNAction.repeatForever(SCNAction.moveBy(x: 5, y: 0, z: 0, duration: 1)))
}
}
}
Try this...
#objc func handleTap(recognizer: UITapGestureRecognizer)
{
if(data.isNavigationOff == true) { return } // No panel select if Add, Update, EndWave, or EndGame
if(gameMenuTableView.isHidden == false) { return } // No panel if game menu is showing
let location: CGPoint = recognizer.location(in: gameScene)
if(data.isAirStrikeModeOn == true)
{
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(location.x, location.y, CGFloat(projectedPoint.z)))
gameControl.airStrike(position: scenePoint)
}
else
{
let hitResults = gameScene.hitTest(location, options: hitTestOptions)
for vHit in hitResults
{
if(vHit.node.name?.prefix(5) == "Panel")
{
// May have selected an invalid panel or auto upgrade was on
if(gameControl.selectPanel(vPanel: vHit.node.name!) == false) { return }
return
}
}
}
}
If Airstrike - Tap drops a bomb on the screen where you touched, translating it to 3D
Else hittest looks for panels which may return multiple results
I make drawing book apps. And I made pencilKit in swift So as an image that I add in this question.
So I can draw several things on the canvasView. But!!! I really wanna take pictures in the photo roll so I wanna use that as the background of my canvasView.
But if canvasView.addSubview(ImageView) is in the code then my gray ImageView is bo on the top of my canvasView so if I draw things, then that things are drawn under the canvasView.
If I use imageView.sendSubviewToBack(canvasView) then nothing happen. I really did know how to insert my background image between RootView and canvasView.
Here are my image and code.
//
// ViewController.swift
// webtoon
//
// Created by 신효근 on 2020/07/13.
// Copyright © 2020 신효근. All rights reserved.
//
import UIKit
import PencilKit
class ViewController: UIViewController, PKCanvasViewDelegate, PKToolPickerObserver {
#IBOutlet weak var canvasView: PKCanvasView!
let canvasWidth : CGFloat = 768
let canvasOverscrollHight : CGFloat = 500
var imageView = UIImageView()
var drawing = PKDrawing()
let picker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
canvasView.delegate = self
canvasView.drawing = drawing
canvasView.alwaysBounceVertical = true
canvasView.allowsFingerDrawing = true
if let window = parent?.view.window,
let toolPicker = PKToolPicker.shared(for: window){
toolPicker.setVisible(true, forFirstResponder:canvasView)
toolPicker.addObserver(canvasView)
canvasView.becomeFirstResponder()
}
self.imageView.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 500)
self.imageView.backgroundColor = .gray
imageView.sendSubviewToBack(canvasView)
}
// override func viewDidLayoutSubviews() {
// let canvasScale = canvasView.bounds.width/canvasWidth
// canvasView.minimumZoomScale = canvasScale
// canvasView.maximumZoomScale = canvasScale
// canvasView.zoomScale = canvasScale
// }
override var prefersHomeIndicatorAutoHidden: Bool {
return true
}
#IBAction func saveDrawingToCameraRoll(_ sender: Any) {
}
#IBAction func addButtonPressed(_ sender: Any) {
self.present(self.picker, animated: true)
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
private func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
var newImage: UIImage? = nil
print("Start")
print(info)
print("end")
if let possibleImage = info["UIImagePickerControllerEditedImage"] as? UIImage { // 수정된 이미지가 있을 경우
newImage = possibleImage
} else if let possibleImage = info["UIImagePickerControllerOriginalImage"] as? UIImage { // 오리지널 이미지가 있을 경우
newImage = possibleImage
}
imageView.image = newImage // 받아온 이미지를 이미지 뷰에 넣어준다.
picker.dismiss(animated: true) // 그리고 picker를 닫아준다.
}
}
Here is my code it works for me
canvasView.isOpaque = false
canvasView.backgroundColor = UIColor.clear
canvasView.delegate = context.coordinator
canvasView.becomeFirstResponder()
let imageView = UIImageView(image: canvasImage)
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
let contentView = canvasView.subviews[0]
contentView.addSubview(imageView)
contentView.sendSubviewToBack(imageView)
Storyboard cutting
The steps that I did were:
Create the + and x buttons in the Controllers
Create simple outlets in the Controllers
Create an action in the Controller for the x (dismiss)-button
#IBAction func button_close_pressed(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
Create a segue on the Storyboard from the + button to the targetViewController.
Create the transition class.
import UIKit
class CircularTransition: NSObject {
var circle = UIView()
var startingPoint = CGPoint.zero {
didSet {
circle.center = startingPoint
}
}
var circleColor = UIColor.red
var duration = 3.0
enum CircularTransitionMode: Int {
case present, dismiss, pop
}
var transitionMode: CircularTransitionMode = .present
}
extension CircularTransition: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
if transitionMode == .present {
if let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) {
let viewCenter = presentedView.center
let viewSize = presentedView.frame.size
circle = UIView()
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.height / 2
circle.center = startingPoint
circle.backgroundColor = circleColor
circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
containerView.addSubview(circle)
presentedView.center = startingPoint
presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
presentedView.alpha = 0
containerView.addSubview(presentedView)
UIView.animate(withDuration: duration, animations: {
self.circle.transform = CGAffineTransform.identity
presentedView.transform = CGAffineTransform.identity
presentedView.alpha = 1
presentedView.center = viewCenter
}, completion: { (success: Bool) in
transitionContext.completeTransition(success)
})
}
} else {
let transitionModeKey = (transitionMode == .pop) ? UITransitionContextViewKey.to : UITransitionContextViewKey.from
if let returningView = transitionContext.view(forKey: transitionModeKey) {
let viewCenter = returningView.center
let viewSize = returningView.frame.size
circle.frame = frameForCircle(withViewCenter: viewCenter, size: viewSize, startPoint: startingPoint)
circle.layer.cornerRadius = circle.frame.size.height / 2
circle.center = startingPoint
UIView.animate(withDuration: duration, animations: {
self.circle.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
returningView.center = self.startingPoint
returningView.alpha = 0
if self.transitionMode == .pop {
containerView.insertSubview(returningView, belowSubview: returningView)
containerView.insertSubview(self.circle, belowSubview: returningView)
}
}, completion: { (success: Bool) in
returningView.center = viewCenter
returningView.removeFromSuperview()
self.circle.removeFromSuperview()
transitionContext.completeTransition(success)
})
}
}
}
func frameForCircle(withViewCenter viewCenter: CGPoint, size viewSize: CGSize, startPoint: CGPoint) -> CGRect {
let xLength = fmax(startPoint.x, viewSize.width - startPoint.x)
let yLength = fmax(startPoint.y, viewSize.height - startPoint.y)
let offsetVector = sqrt(xLength * xLength + yLength * yLength) * 2
let size = CGSize(width: offsetVector, height: offsetVector)
return CGRect(origin: CGPoint.zero, size: size)
}
}
In the source Controller I added ... to the class
UIViewControllerTransitioningDelegate
Then
let transition = CircularTransition()
And last but not least
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let eventVC = segue.destination as! EventViewController
eventVC.transitioningDelegate = self
eventVC.modalPresentationStyle = .custom
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .present
transition.startingPoint = self.button_addEvent.center
transition.circleColor = self.button_addEvent.backgroundColor!
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.transitionMode = .dismiss
transition.startingPoint = self.button_addEvent.center
transition.circleColor = self.button_addEvent.backgroundColor!
return transition
}
Now, why doesn't that work? I only can think of that the problem comes with the Navigation Controller instead of a normal ViewController, because with a normal one it works fine. But I really have no idea hwo to change the code to match with the Navigation Controller (source Controller).
Kind regards and thank you!
You need to implement UINavigationControllerDelegate in your viewController
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push:
transition.transitionMode = .present
transition.startingPoint = self.button_addEvent.center
transition.circleColor = self.button_addEvent.backgroundColor!
return transition
default:
transition.transitionMode = .dismiss
transition.startingPoint = self.button_addEvent.center
transition.circleColor = self.button_addEvent.backgroundColor!
return transition
}
}
I have a custom view subclassing NSView, which is just an NSStackView containing a label, slider, a second label and a checkbox. The slider and checkbox are both configured to report changes to the view (and eventually, via a delegate to a ViewController):
fileprivate extension NSTextField {
static func label(text: String? = nil) -> NSTextField {
let label = NSTextField()
label.isEditable = false
label.isSelectable = false
label.isBezeled = false
label.drawsBackground = false
label.stringValue = text ?? ""
return label
}
}
#IBDesignable
class Adjustable: NSView {
private let sliderLabel = NSTextField.label()
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let valueLabel = NSTextField.label()
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
var valueFormatter: (Double)->(String) = { String(format:"%5.2f", $0) }
...
#objc func sliderChanged(_ sender: Any) {
guard let slider = sender as? NSSlider else { return }
valueLabel.stringValue = valueFormatter(slider.doubleValue)
print("Slider now: \(slider.doubleValue)")
delegate?.adjustable(self, changedValue: slider.doubleValue)
}
#objc func enabledChanged(_ sender: Any) {
guard let checkbox = sender as? NSButton else { return }
print("Enabled now: \(checkbox.state == .on)")
delegate?.adjustable(self, changedEnabled: checkbox.state == .on)
}
}
Using InterfaceBuilder, I can add one instance of this to a ViewController by dragging in a CustomView and setting it's class in the Identity Inspector. Toggling the checkbox or changing the slider will have the desired effect.
However, if I have multiple instances then in the target-action functions self will always refer to the same instance of the view, rather than the one being interacted with. In other words, self.slider == sender is only true in sliderChanged for one of the sliders. While I can get the correct slider value via sender, I cannot update the correct label as self.valueLabel is always the label in the first instance of the custom view.
Incidentally, #IBDesignable and the code intended to support it have no effect so there's something I'm missing there too - Interface Builder just shows empty space.
The whole file:
import Cocoa
fileprivate extension NSTextField {
static func label(text: String? = nil) -> NSTextField {
let label = NSTextField()
label.isEditable = false
label.isSelectable = false
label.isBezeled = false
label.drawsBackground = false
label.stringValue = text ?? ""
return label
}
}
protocol AdjustableDelegate {
func adjustable(_ adjustable: Adjustable, changedEnabled: Bool)
func adjustable(_ adjustable: Adjustable, changedValue: Double)
}
#IBDesignable
class Adjustable: NSView {
var delegate: AdjustableDelegate? = nil
private let sliderLabel = NSTextField.label()
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let valueLabel = NSTextField.label()
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
var valueFormatter: (Double)->(String) = { String(format:"%5.2f", $0) }
#IBInspectable
var label: String = "" {
didSet {
sliderLabel.stringValue = label
}
}
#IBInspectable
var value: Double = 0 {
didSet {
slider.doubleValue = value
valueLabel.stringValue = valueFormatter(value)
}
}
#IBInspectable
var enabled: Bool = false {
didSet {
enabledCheckbox.isEnabled = enabled
}
}
#IBInspectable
var minimum: Double = 0 {
didSet {
slider.minValue = minimum
}
}
#IBInspectable
var maximum: Double = 100 {
didSet {
slider.maxValue = maximum
}
}
#IBInspectable
var tickMarks: Int = 0
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder decoder: NSCoder) {
super.init(coder: decoder)
setup()
}
override func prepareForInterfaceBuilder() {
setup()
}
override func awakeFromNib() {
setup()
}
private func setup() {
let stack = NSStackView()
stack.orientation = .horizontal
stack.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(sliderLabel)
stack.addArrangedSubview(slider)
stack.addArrangedSubview(valueLabel)
stack.addArrangedSubview(enabledCheckbox)
sliderLabel.stringValue = label
slider.doubleValue = value
valueLabel.stringValue = valueFormatter(value)
slider.minValue = minimum
slider.maxValue = maximum
slider.numberOfTickMarks = tickMarks
// Make the slider be the one that expands to fill available space
slider.setContentHuggingPriority(NSLayoutConstraint.Priority(rawValue: 249), for: .horizontal)
sliderLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
valueLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
addSubview(stack)
stack.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
stack.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
stack.topAnchor.constraint(equalTo: topAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
#objc func sliderChanged(_ sender: Any) {
guard let slider = sender as? NSSlider else { return }
valueLabel.stringValue = valueFormatter(slider.doubleValue)
print("Slider now: \(slider.doubleValue)")
delegate?.adjustable(self, changedValue: slider.doubleValue)
}
#objc func enabledChanged(_ sender: Any) {
guard let checkbox = sender as? NSButton else { return }
print("Enabled now: \(checkbox.state == .on)")
delegate?.adjustable(self, changedEnabled: checkbox.state == .on)
}
}
The solution, as described in the question linked by Willeke, was to ensure init had completed before referencing self. (I'm slightly surprised the compiler allowed it to be used in a property initialiser)
Wrong:
private let slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private let enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))
Right:
private lazy var slider = NSSlider(target: self, action: #selector(sliderChanged(_:)))
private lazy var enabledCheckbox = NSButton(checkboxWithTitle: "Enabled", target: self, action: #selector(enabledChanged(_:)))